文章目录
- 前言
- Springboot框架准备知识
- java beans
- bean的使用方式
- 上下文context
- 启动过程
- SpringApplication的构造函数
- run函数
- 监听器
- SpringApplicationRunListeners类
- ApplicationEvent事件类
- ApplicationListener类(观察者)
- 上下文context
- ConfigurableEnvironment类
- MutablePropertySources类
- 启动内嵌的tomcat服务器
- 启动时的注解
- @SpringBootApplication注解
- @Component/@Controller/@Service/@Repository
- 总结
前言
很长时间没有接触java web开发了,最近捡起来开始做一些小项目。用的是springboot框架,一开始用就有一个疑问,以往都是利用的war包,然后将war包部署到tomcat容器中运行。也正是因为java体系里面有太多的框架给我们完成了大量的工作,以至于很多底层的原理性的内容都被隐藏了,想要进一步理解,就必须刨根问底的去找答案。
首先肯定是从启动开始,通过springboot脚手架工具创建一个web应用,再打开主类,主类里面一般就一行代码:
@SpringBootApplication
public class WebJarApplication {
public static void main(String[] args) {
SpringApplication.run(WebJarApplication.class, args);
}
}
很明显,所有的内容都被封装在了SpringApplication类里面了,那这篇文章主要的内容就是来说一说这个SpringApplication类里面的内容。
Springboot框架准备知识
java beans
我自己的理解,整个springboot框架是一个大的盒子,这个盒子里面装的全部是一个一个的类的实例,在java体系里面把这些实例成为java bean,也可以称作bean。而框架通过DI机制把这些bean注入到容器中。由容器来确认beans之间的依赖。或者可以理解为容器是一个可以容纳各种乐高积木的架子,而一个一个的bean就是一块一块的乐高积木。而DI这个机制可以保证程序在运行的时候都可以往这个架子里面塞乐高积木。
在当年的J2EE时代,Java体系中确定了一个beans的标准:web application标准,包含了下面的一些领域(我理解就是各种不同应用的beans):
- JDBC(Java Database Connectivity)
- JNDI(Java Name and Directory Interface)
- EJB(Enterprise JavaBean)
- RMI(Remote Method Invoke)
- Java IDL/CORBA(通用对象请求代理架构是软件构建的一个标准 )
- JSP
- Java Servlet
- XML
- JMS
- JTA
- JTS
- JavaMail
- JAF(JavaBeans Activation Framework)
这些标准在这么多年的演进过程中,有得得以发扬光大,有点慢慢淡出业界了。和网络中的OSI七层标准和业界实际的TCP/IP标准类似,谁能满足市场要求才有生命力。
容器的概念是从上面的一些标准中衍生出来,比如javaBean/EJB(Entrprise javaBean)/web容器。给处于其中的应用程序组件(JSP、Servlet)提供一个环境,使得JSP,Servlet能直接和容器中的环境变量、接口交互而不必关注其他系统问题,现在的容器更多的连servlet等标准功能全部实现好了,而且JSP基本上也销声匿迹了。
bean的使用方式
这个简单提一下。
- 老一代的xml配置方式:
<bean id="person" class="me.sjl.bean.Person">
<property name="age" value="18"/>
<property name="name" value="sjl"/>
</bean>
- spring中的注解配置方式:
通过@Bean注解实现,配合@Configuration注解(声明这个类是一个配置类,相当于就是上一点提到的xml文件,可以被加载)。
@Configuration
public class BeanConfig {
@Bean
public Person person() {
return new Person("SJL01", 20);
}
}
还有些factory的用法就不说了,这个不是这篇文章的重点,有兴趣的可以自行了解。
上下文context
上面提到了bean,就是一个一个的组件。而这个上下文就是装组件的盒子。在后续启动过程的章节中会详细说到怎么组装这个上下文,但是这个上下文是非常复杂的一个类,所以这里先做点铺垫,在springboot的启动源码中,使用到的上下文类是ConfigurableApplicationContext,这是一个接口。
ConfigurableApplicationContext context = null;
可以看一下这张图的类图:
下面的这个ConfigurableWebApplicationContext在后续章节会提到。实际上后续还有一大把的实现类。
启动过程
话不多说,直接看源码。在SpringApplication这个类中,首先和启动相关的就是两个函数:
- 类的构造函数,初始化一些成员变量
- run函数,将springboot程序启动起来
SpringApplication的构造函数
先来看下构造函数,代码如下:
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.sources = new LinkedHashSet();
this.bannerMode = Mode.CONSOLE;
this.logStartupInfo = true;
this.addCommandLineProperties = true;
this.addConversionService = true;
this.headless = true;
this.registerShutdownHook = true;
this.additionalProfiles = Collections.emptySet();
this.isCustomEnvironment = false;
this.lazyInitialization = false;
this.applicationContextFactory = ApplicationContextFactory.DEFAULT;
this.applicationStartup = ApplicationStartup.DEFAULT;
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
this.webApplicationType = WebApplicationType.deduceFromClasspath();
this.bootstrapRegistryInitializers = this.getBootstrapRegistryInitializersFromSpringFactories();
this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = this.deduceMainApplicationClass();
}
还有一些其他的重载函数,我理解是为了兼容一些其他版本做的事情,最后都会进入这个函数来进行处理。看一下里面几行重要的代码:
- 确定应用类型webApplicationType:
this.webApplicationType = WebApplicationType.deduceFromClasspath()
再看一下这个deduceFromClasspath函数:
static WebApplicationType deduceFromClasspath() {
if (ClassUtils.isPresent("org.springframework.web.reactive.DispatcherHandler", (ClassLoader)null) && !ClassUtils.isPresent("org.springframework.web.servlet.DispatcherServlet", (ClassLoader)null) && !ClassUtils.isPresent("org.glassfish.jersey.servlet.ServletContainer", (ClassLoader)null)) {
return REACTIVE;
} else {
String[] var0 = SERVLET_INDICATOR_CLASSES;
int var1 = var0.length;
for(int var2 = 0; var2 < var1; ++var2) {
String className = var0[var2];
if (!ClassUtils.isPresent(className, (ClassLoader)null)) {
return NONE;
}
}
return SERVLET;
}
}
实际上就是看一下classpath中有哪些类,里面有几种类型:
- SERVLET:普通的web容器应用
- REACTIVE:webFlux应用,非阻塞型IO,也就是不是一个请求一个线程来处理。没接触过,好像很厉害的东西。
- NONE:普通java应用。
- 选择上下文构造器:this.applicationContextFactory = ApplicationContextFactory.DEFAULT; ApplicationContextFactory.DEFAULT是一个lambda表达式,实际上就是一个函数指针,也即是说成员变量this.applicationContextFactory就是一个指针,指向了一个函数体,后续可以通过这个指针直接执行这个函数:。不了解lambda表达式的可以去网上先了解一下,ApplicationContextFactory就是一个lambda表达式的接口。
ApplicationContextFactory DEFAULT = (webApplicationType) -> {
try {
switch(webApplicationType) {
case SERVLET:
return new AnnotationConfigServletWebServerApplicationContext();
case REACTIVE:
return new AnnotationConfigReactiveWebServerApplicationContext();
default:
return new AnnotationConfigApplicationContext();
}
} catch (Exception var2) {
throw new IllegalStateException("Unable create a default ApplicationContext instance, you may need a custom ApplicationContextFactory", var2);
}
};
这个函数接受一个参数:webApplicationType,也就是上面提到的这个变量。这个函数根据这个变量的值返回一个上下文的工厂类,当然这个函数的执行是在run函数中执行的,这里先讲一下这个逻辑,在run函数的时候再详细说一下返回的这个AnnotationConfigServletWebServerApplicationContext类。
- getSpringFactoriesInstances方法:
接下来的两行代码,是获取初始化工厂类与监听器类。
this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
这里主要为了介绍getSpringFactoriesInstances方法,上下文的初始化过程与监听器都在run函数的部分再说。
看一下getSpringFactoriesInstances的处理代码:
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = this.getClassLoader();
Set<String> names = new LinkedHashSet(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
List<T> instances = this.createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
其中的SpringFactoriesLoader.loadFactoryNames函数最终会调用loadSpringFactories方法(为了篇幅就不全部贴代码了),其中有一行代码:
Enumeration urls = classLoader.getResources("META-INF/spring.factories");
就是从jar包中访问这个文件,这个文件里面记录了相关的方法。因为我的工程中没有自定义这个文件,所以直接调用的是springboot.jar中的方法。
我的springboot的jar包是在maven的仓库里,打开这个jar包的spring.factories文件可以找到:
# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.ClearCachesApplicationListener,\
org.springframework.boot.builder.ParentContextCloserApplicationListener,\
org.springframework.boot.context.FileEncodingApplicationListener,\
org.springframework.boot.context.config.AnsiOutputApplicationListener,\
org.springframework.boot.context.config.DelegatingApplicationListener,\
org.springframework.boot.context.logging.LoggingApplicationListener,\
org.springframework.boot.env.EnvironmentPostProcessorApplicationListener,\
org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener
# Application Context Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
org.springframework.boot.context.ContextIdApplicationContextInitializer,\
org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
org.springframework.boot.rsocket.context.RSocketPortInfoApplicationContextInitializer,\
org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer
也就是说spring框架会为这些类都创建一个实例。
public void setInitializers(Collection<? extends ApplicationContextInitializer<?>> initializers) {
this.initializers = new ArrayList(initializers);
}
public void setListeners(Collection<? extends ApplicationListener<?>> listeners) {
this.listeners = new ArrayList(listeners);
}
调试一下看一下,从截图中可以看出,spring框架通过IOC机制将所有的类全部实例华后注入到了容器中。
- 确定主类:
this.mainApplicationClass = this.deduceMainApplicationClass();
run函数
run函数主要就做了两件事情:
- 准备好上下文,也就是把springboot这个框架里面该有的东西全部填充好。后续代码就可以通过上下文获取到这些信息,或者自动进行注入或者自动装配。
- 准备好监听器,准备好框架中的事件模型。
监听器
事件驱动实际上就是一种观察者模式,由事件发布者发布事件,再由观察者根据条件讲相应的事件领走。
在SpringApplication类中就存在这两种角色。
- ApplicationListener类。这个类作为了SpringApplication类的成员变量:
private List<ApplicationListener<?>> listeners;
这个类相当于观察者,是事件的领取和消费者,或者说执行者。
- SpringApplicationRunListeners类。这个类作为run函数的局部变量:
SpringApplicationRunListeners listeners = this.getRunListeners(args);
这个类相当于发布者,是事件的发起人。
SpringApplicationRunListeners类
先看下事件的发布者。根据上面的代码,看一下getRunListeners函数代码:
private SpringApplicationRunListeners getRunListeners(String[] args) {
Class<?>[] types = new Class[]{SpringApplication.class, String[].class};
return new SpringApplicationRunListeners(logger, this.getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args), this.applicationStartup);
}
可以看出,这个也是通过getSpringFactoriesInstances函数,也就是从spring.factories文件里获取到类的信息再实例化的东西。spring.factories文件里的内容是:
# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener。
所以打开EventPublishingRunListener类看一下:
就是继承自SpringApplicationRunListeners。看一下这个类的构造函数:
public EventPublishingRunListener(SpringApplication application, String[] args) {
this.application = application;
this.args = args;
this.initialMulticaster = new SimpleApplicationEventMulticaster();
Iterator var3 = application.getListeners().iterator();
while(var3.hasNext()) {
ApplicationListener<?> listener = (ApplicationListener)var3.next();
this.initialMulticaster.addApplicationListener(listener);
}
}
- 在这个类中定义了一个广播器:SimpleApplicationEventMulticaster,这个广播器用于广播事件。
- 将application中所有的事件接收者,或者说是观察者添加到这个广播器中来。
再看一下run函数中对这个类的使用,总共有3个地方:
listeners.starting(bootstrapContext, this.mainApplicationClass);
listeners.started(context);
listeners.running(context);
- listeners是的这几个方法就是循环调用列表中的listener的响应方法。
- 这几个方法都类似,就是发布不同的事件出来。
public void starting(ConfigurableBootstrapContext bootstrapContext) {
this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(bootstrapContext, this.application, this.args));//ApplicationStartingEvent事件
}
public void started(ConfigurableApplicationContext context) {
context.publishEvent(new ApplicationStartedEvent(this.application, this.args, context));//ApplicationStartedEvent事件
AvailabilityChangeEvent.publish(context, LivenessState.CORRECT);
}
public void running(ConfigurableApplicationContext context) {
context.publishEvent(new ApplicationReadyEvent(this.application, this.args, context));//ApplicationReadyEvent事件
AvailabilityChangeEvent.publish(context, ReadinessState.ACCEPTING_TRAFFIC);
}
ApplicationEvent事件类
上面代码中的ApplicationStartingEvent/ApplicationStartedEvent/ApplicationReadyEvent都是spring框架里面的事件类。
基本上是一致调用父类的构造函数,给到EventObject类的source成员变量里。
public EventObject(Object source) {
if (source == null)
throw new IllegalArgumentException("null source");
this.source = source;
}
在springboot的框架里,这个source就是this.application(SpringApplication类)
ApplicationListener类(观察者)
这一系列的类很多,从spring.factories里面就可以看到,因为这个类同样也是通过getSpringFactoriesInstances函数获得的。在构造函数的部分已经说过了。
看看这一大把的监听器:
这些类都实现了ApplicationListener的onApplicationEvent函数。根据不同的事件类型来做相应的处理。spring.factories里面的那几个监听器的作用如下:
监听器 | 作用 |
ClearCachesApplicationListener | 应用上下文加载完成后对缓存做清除工作,响应事件ContextRefreshedEvent |
ParentContextCloserApplicationListener | 监听双亲应用上下文的关闭事件并往自己的孩子应用上下文中传播,相关事件 |
FileEncodingApplicationListener | 如果系统文件编码和环境变量中指定的不同则终止应用启动。 具体的方法是比较系统属性 |
AnsiOutputApplicationListener | 根据 |
ConfigFileApplicationListener | EnvironmentPostProcessor,从常见的那些约定的位置读取配置文件,比如从以下目录读取application.properties,application.yml等配置文件:classpath: file:. classpath:config file:./config/: 也可以配置成从其他指定的位置读取配置文件 |
DelegatingApplicationListener | 监听到事件后转发给环境变量 |
LoggingApplicationListener | 配置 |
LiquibaseServiceLocatorApplicationListener | 使用一个可以和Spring Boot可执行jar包配合工作的版本替换liquibase ServiceLocator |
上下文context
先来说说上下文。
- ConfigurableApplicationContext类。springboot框架中用到的上下文是ConfigurableApplicationContext类,这个是个接口。在run函数中的代码如下:
ConfigurableApplicationContext context = null;
context = this.createApplicationContext();
protected ConfigurableApplicationContext createApplicationContext() {
return this.applicationContextFactory.create(this.webApplicationType);
}
这里就用到了在构造函数部分提到的lambda表达式,调用ApplicationContextFactory的lambda接口create函数,也就是this.applicationContextFactory这个函数指针指向的函数体。
这个函数的逻辑很简单,我们的webApplicationType是SERVLET,那么返回的对象就是AnnotationConfigServletWebServerApplicationContext类的实例。
- AnnotationConfigServletWebServerApplicationContext类
这个类是从ConfigurableApplicationContext接口衍生出来的,贴一张更复杂的类图:
- 准备实例中的内容
this.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
这个函数就是准备上下文中的内容。
private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
context.setEnvironment(environment);
this.postProcessApplicationContext(context);
this.applyInitializers(context);
listeners.contextPrepared(context);
bootstrapContext.close(context);
if (this.logStartupInfo) {
this.logStartupInfo(context.getParent() == null);
this.logStartupProfileInfo(context);
}
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
if (beanFactory instanceof DefaultListableBeanFactory) {
((DefaultListableBeanFactory)beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
if (this.lazyInitialization) {
context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
}
Set<Object> sources = this.getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
this.load(context, sources.toArray(new Object[0]));
listeners.contextLoaded(context);
}
从代码中来看,context的内容就是由ConfigurableEnvironment/SpringApplicationRunListeners/ApplicationArguments/Banner四部分内容组成,所以我们必须先来看看这几部分的内容。
ConfigurableEnvironment类
环境类,顾名思义就是包含了很多环境变量和信息的类。看一下类图
可以看出,环境信息主要就是包括了各种属性,通过PropertyResolver来解析各种属性。
和上下文类似,ConfigurableEnvironment实例也是需要根据webApplicationType来确定的。
ConfigurableEnvironment environment = this.getOrCreateEnvironment();
private ConfigurableEnvironment getOrCreateEnvironment() {
if (this.environment != null) {
return this.environment;
} else {
switch(this.webApplicationType) {
case SERVLET:
return new StandardServletEnvironment();
case REACTIVE:
return new StandardReactiveWebEnvironment();
default:
return new StandardEnvironment();
}
}
}
那么我们得到的就是StandardServletEnvironment类的实例。看一下类图:
在继承的AbstractEnvironment类中,有下面几个成员变量:
private final Set<String> activeProfiles;
private final Set<String> defaultProfiles;
private final MutablePropertySources propertySources;
而这个抽象类的构造函数中,有下面的代码:
protected AbstractEnvironment(MutablePropertySources propertySources) {
this.logger = LogFactory.getLog(this.getClass());
this.activeProfiles = new LinkedHashSet();
this.defaultProfiles = new LinkedHashSet(this.getReservedDefaultProfiles());
this.propertySources = propertySources;
this.propertyResolver = this.createPropertyResolver(propertySources);
this.customizePropertySources(propertySources);
}
而在StandardServletEnvironment类中有下面的代码(继承关系会调用到的):
protected void customizePropertySources(MutablePropertySources propertySources) {
propertySources.addLast(new StubPropertySource("servletConfigInitParams"));
propertySources.addLast(new StubPropertySource("servletContextInitParams"));
if (JndiLocatorDelegate.isDefaultJndiEnvironmentAvailable()) {
propertySources.addLast(new JndiPropertySource("jndiProperties"));
}
super.customizePropertySources(propertySources);
}
在StandardServletEnvironment的父类StandardEnvironment中有下面的代码:
protected void customizePropertySources(MutablePropertySources propertySources) {
propertySources.addLast(new PropertiesPropertySource("systemProperties", this.getSystemProperties()));
propertySources.addLast(new SystemEnvironmentPropertySource("systemEnvironment", this.getSystemEnvironment()));
}
所以在最终的StandardServletEnvironment类实例中会有4类属性值:
- systemProperties
- systemEnvironment
- servletConfigInitParams
- servletContextInitParams
这些属性值就是通过各种各样的途径获取到的环境变量参数,基本上都是些系统或者虚拟机自带的一些参数。
MutablePropertySources类
在Environment类里面使用到的属性容器类就是MutablePropertySources。这个类实现了PropertySources接口,主要容器就是List<PropertySource<?>>列表。所以再看看PropertySource类。
public abstract class PropertySource<T> {
protected final Log logger;
protected final String name;
protected final T source;
上面的代码里实际上是创建了如下几种PropertySource
- SystemEnvironmentPropertySource
- PropertiesPropertySource
- StubPropertySource
- JndiPropertySource
这些都是从PropertySource派生出来的实现类,从idea的继承图中可以看出,有各种各样的派生实现类:
启动内嵌的tomcat服务器
环境的启动肯定要包含servlet服务器的启动,这个启动的动作在run函数的refreshContext中。
this.refreshContext(context);
private void refreshContext(ConfigurableApplicationContext context) {
if (this.registerShutdownHook) {
try {
context.registerShutdownHook();
} catch (AccessControlException var3) {
}
}
this.refresh((ApplicationContext)context);
}
//一直跟踪到这个函数
protected void refresh(ConfigurableApplicationContext applicationContext) {
applicationContext.refresh();
}
上面已经提到了ConfigurableApplicationContext这个类,最终工厂返回的是AnnotationConfigServletWebServerApplicationContext这个类的实例,那么打开这个类的代码看一下,会发现这个refresh代码是位于它的父类ServletWebServerApplicationContext里面:
public final void refresh() throws BeansException, IllegalStateException {
try {
super.refresh();
} catch (RuntimeException var3) {
WebServer webServer = this.webServer;
if (webServer != null) {
webServer.stop();
}
throw var3;
}
}
一直要跟踪到AbstractApplicationContext这一层才看到:
public void refresh() throws BeansException, IllegalStateException {
synchronized(this.startupShutdownMonitor) {
StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
this.prepareRefresh();
ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
this.prepareBeanFactory(beanFactory);
try {
this.postProcessBeanFactory(beanFactory);
StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
this.invokeBeanFactoryPostProcessors(beanFactory);
this.registerBeanPostProcessors(beanFactory);
beanPostProcess.end();
this.initMessageSource();
this.initApplicationEventMulticaster();
this.onRefresh();
this.registerListeners();
this.finishBeanFactoryInitialization(beanFactory);
this.finishRefresh();
这个里面又通过this.onRefresh()会往子类调用,再回头找,最后找到在ServletWebServerApplicationContext类中的这个onRefresh方法:
protected void onRefresh() {
super.onRefresh();
try {
this.createWebServer();
} catch (Throwable var2) {
throw new ApplicationContextException("Unable to start web server", var2);
}
}
private void createWebServer() {
WebServer webServer = this.webServer;
ServletContext servletContext = this.getServletContext();
if (webServer == null && servletContext == null) {
StartupStep createWebServer = this.getApplicationStartup().start("spring.boot.webserver.create");
ServletWebServerFactory factory = this.getWebServerFactory();
createWebServer.tag("factory", factory.getClass().toString());
this.webServer = factory.getWebServer(new ServletContextInitializer[]{this.getSelfInitializer()});
createWebServer.end();
this.getBeanFactory().registerSingleton("webServerGracefulShutdown", new WebServerGracefulShutdownLifecycle(this.webServer));
this.getBeanFactory().registerSingleton("webServerStartStop", new WebServerStartStopLifecycle(this, this.webServer));
} else if (servletContext != null) {
try {
this.getSelfInitializer().onStartup(servletContext);
} catch (ServletException var5) {
throw new ApplicationContextException("Cannot initialize servlet context", var5);
}
}
this.initPropertySources();
}
ServletWebServerFactory这个类同样是个工厂类,实现类有三个。
我们这里配置的是tomcat,打开tomcat类看一下,熟悉的感觉终于出现了:
public WebServer getWebServer(ServletContextInitializer... initializers) {
if (this.disableMBeanRegistry) {
Registry.disableRegistry();
}
Tomcat tomcat = new Tomcat();
File baseDir = this.baseDirectory != null ? this.baseDirectory : this.createTempDir("tomcat");
tomcat.setBaseDir(baseDir.getAbsolutePath());
Connector connector = new Connector(this.protocol);
connector.setThrowOnFailure(true);
tomcat.getService().addConnector(connector);
this.customizeConnector(connector);
tomcat.setConnector(connector);
tomcat.getHost().setAutoDeploy(false);
this.configureEngine(tomcat.getEngine());
Iterator var5 = this.additionalTomcatConnectors.iterator();
while(var5.hasNext()) {
Connector additionalConnector = (Connector)var5.next();
tomcat.getService().addConnector(additionalConnector);
}
this.prepareContext(tomcat.getHost(), initializers);
return this.getTomcatWebServer(tomcat);
}
这样就把tomcat服务器启动起来了。
启动时的注解
上面的过程就是将springboot的基本框架全部启动起来了,那我们自己写的那些类是怎么启动的呢?那就是依靠SpringBootApplication注解了。
@SpringBootApplication注解
这个注解基本上就等于下面三个注解的总和
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan
在提这三个注解的时候,稍微扩展一下,接着上面提到的@Bean注解讲。
@Component/@Controller/@Service/@Repository
先提一下Component注解:
@Componnet //相当于<bean class="DemoImpl">
public class DemoImpl implements demoService{
public void test(){
dosomething();
return;
}
}
就是相当于之前的xml文件里配置一个bean类。@Componnet注解和之前的@Bean注解
而在最前面提到的各种各样的bean,在spring容器里提供了@Controller/@Service/@Repository等各种不同的注解来表示。
而上面提到的三个注解就负责分工合作,将我们自定义的所有@Controller/@Service/@Repository等各种定义的bean扫描,解析再注入到spring框架中,提供给springapplication使用。
总结
通过SpringApplication类的构造方法,run函数可以将springboot的基本框架跑起来;然后再通过@SpringBootApplication注解将我们自己自定义的所有bean加载到框架中,springboot的应用就启动起来了。