文章目录
- Q1、谈谈对SpringBoot的理解,它有哪些特性?
- Q2、Spring和SpringBoot的关系和区别是什么?
- Q3、SpringBoot的核心注解
- Q4、SpringBoot的自动配置原理
- [Q5、为什么SpringBoot的jar包可以直接java -jar运行?](#Q5、为什么SpringBoot的jar包可以直接java -jar运行?)
- Q6、SpringBoot的启动原理?
- Q7、SpringBoot内置Tomcat的启动原理是什么?
- Q8、SpringBoot外部Tomcat启动原理?
- Q9、如何自定义一个Starter?
- Q10、SpringBoot读取配置文件的原理是什么?加载顺序是怎样的?
Q1、谈谈对SpringBoot的理解,它有哪些特性?
答案:
SpringBoot的用来快速开发Spring应用的一个脚手架 、其设计目的是用来简化新spring应用的初始搭建以及开发过程。
- SpringBoot提供了很多内置的starter结合自动配置,对主流框架无配置集成、开箱即用
- SpringBoot简化了开发,采用Javaconfig的方式可以使用零xml的方式进行开发
- SpringBoot内置web容器无需依赖外部wep服务器,省略了web.xml,直接运行jar文件就可以启动web应用
- SpringBoot帮我管理了常用的第三方依赖的版本,减少出现版本冲突的问题
- SpringBoot自带了监控功能,可以监控应用程序的运行状况,或者内存、线程池、Htp 请求统计等,同时还提供了优雅关闭应用程序等功能
Q2、Spring和SpringBoot的关系和区别是什么?
答案:
- SpringBoot是Spring生态的产品
- Spring Framework是一个容器框架
SpringBoot 它不是一个框架
、它是一个可以快速构建基于Spring的脚手架(里面包含了Spring和各种框架
),为开发Spring生态其他框架铺平道路
二者不是一个层面的东西,没有可比性。
Q3、SpringBoot的核心注解
答案:
-
@SpringBootApplication :常用于启动类,标记这个应用是一个SpringBoot应用
-
@SpringBootConfiguration:这个注解其实就是@Configuration,表示启动类是一个配置类
-
@EnableAutoConfiguration :向Spring容器中导入一个Selector,用来加载类路径下的SpringFactories中定义的自动配置类,并将这些自动加载为配置Bean
此外,SpringBoot还有一些注解,用于进行定制开发(当满足什么条件时才启用):
-
@ConditionalOnBean
-
@ConditionalOnMissingBean
-
@ConditionalOnClass
-
@ConditionalOnExpression
-
@ConditionalOnMissingBean
-
...
Q4、SpringBoot的自动配置原理
答案:
- 通过@SpringBootApplication 引入了
@EnableAutoConfiguration
(负责启动自动配置功能) - @EnableAutoConfiguration 引入了
@lmport
- Spring容器启动时(加载loc容器时)会解析@Import 注解
- 上面的@lmport导入了一个
DeferredlmportSelector
,它会使SpringBoot的自动配置类的顺序在最后,这样方便我们扩展和覆盖 - 然后读取所有的
/META-INF/spring.factories
文件 - 过滤出所有AutoConfigurtionClass类型的类
- 最后通过@ConditionOnXXX排除无效的自动配置类
Q5、为什么SpringBoot的jar包可以直接java -jar运行?
普通的jar包直接运行,报错:jar包中没有主清单属性
答案:
-
SpringBoot提供了一个插件spring-boot-maven-plugin,用于把程序打包成一个可执行的jar包
-
SpringBoot应用打包后,生成一个Fat jar(jar包中包含jar),包含了应用依赖的jar包和SpringBoot loader相关的类
-
java -jar命令执行时,会去jar中找manifest文件,在那里面找到真正的启动类
-
Fat jar的启动Main方法是JarLauncher,它负责创建一个LaunchedURLClassLoader来加载boot-lib下面所依赖的那些jar,并以一个新线程启动应用的Main方法(找到manifest文件中的Start-Class)
Q6、SpringBoot的启动原理?
此问题即run方法背后在做些什么?
答案:
-
运行main方法:初始化SpringApplication,从spring.factories中读取listener、ApplicationContextInitializer
-
运行run方法
-
读取环境变量、配置信息...
-
创建SpringApplication上下文:
ServletWebServerApplicationContext
-
预初始化上下文 :读取启动类,将启动类做为配置类读取,注册为BeanDefinition
-
调用
refresh方法
加载IoC容器- invokeBeanFactoryPostProcessor -- 解析@lmport: 加载所有的自动配置类
- onRefresh 创建(内置)servlet容器(默认tomcat)
-
在这个过程中springboot会调用很多监听器对外进行扩展
Q7、SpringBoot内置Tomcat的启动原理是什么?
答案:
- 当添加了spring-boot-starter-web依赖后,SpringBoot中会添加
ServletWebServerFactoryAutoConfiguration
servlet容器自动配置类 - 这个自动配置类通过@Import导入了可用的一个web容器工厂,默认tomcat(通过@ConditionalOnClass判断决定使用哪一个)
- 在内嵌Tomcat类中配置了一个TomcatServletWebServerFactory的Bean (Web容器工厂)
- 它会在SpringBoot启动时,加载ioc容器(refresh) OnRefersh 创建内嵌的Tomcat并启动
Q8、SpringBoot外部Tomcat启动原理?
先说下实现:
- 在pom文件中将打包方式改为war(后续要交给外部的tomcat运行)
java
<packaging>war</packaging>
- 将内置Tomcat的作用范围修改成provided
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
- 自定义一个类继承 SpringBootServletInitializer 重写其configure()方法,并传入SpringBoot项目主程序的类
java
public class ServletInitializer extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
// 传入SpringBoot的主程序类
return builder.sources(DemoApplication.class);
}
}
答案:
- 根据Servlet3.0规范,服务器启动(web应用启动)会创建当前web应用里面每一个jar包里面ServletContainerInitializer实例
- ServletContainerInitializer的实现放在jar包的META-INF/services文件夹下,有一个名为javax.servlet.ServletContainerInitializer的文件,内容就是ServletContainerInitializer的实现类的全类名
- @HandlesTypes注解,在应用启动的时候加载我们感兴趣的类WebApplicationInitializer.class,上面自己继承的类的父类就是WebApplicationInitializer.class
java
@HandlesTypes({WebApplicationInitializer.class})
public class SpringServletContainerInitializer implements ServletContainerInitializer {
public SpringServletContainerInitializer() {
}
public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext) throws ServletException {
List<WebApplicationInitializer> initializers = new LinkedList();
Iterator var4;
if (webAppInitializerClasses != null) {
var4 = webAppInitializerClasses.iterator();
while(var4.hasNext()) {
Class<?> waiClass = (Class)var4.next();
if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) && WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
try {
// 将@HandlesTypes(WebApplicationInitializer.class)标注的所有这个类型的类都传入到onStartup方法的Set<Class<?>>;为这些WebApplicationInitializer类型的类创建实例。
initializers.add((WebApplicationInitializer)ReflectionUtils.accessibleConstructor(waiClass, new Class[0]).newInstance());
} catch (Throwable var7) {
throw new ServletException("Failed to instantiate WebApplicationInitializer class", var7);
}
}
}
}
if (initializers.isEmpty()) {
servletContext.log("No Spring WebApplicationInitializer types detected on classpath");
} else {
servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath");
AnnotationAwareOrderComparator.sort(initializers);
var4 = initializers.iterator();
while(var4.hasNext()) {
WebApplicationInitializer initializer = (WebApplicationInitializer)var4.next();
// 每一个WebApplicationInitializer都调用自己的onStartup()
initializer.onStartup(servletContext);
}
}
}
}
- onStartup方法,创建web应用容器
java
public abstract class SpringBootServletInitializer implements WebApplicationInitializer {
public void onStartup(ServletContext servletContext) throws ServletException {
this.logger = LogFactory.getLog(this.getClass());
// 创建web应用容器
WebApplicationContext rootAppContext = this.createRootApplicationContext(servletContext);
if (rootAppContext != null) {
servletContext.addListener(new ContextLoaderListener(rootAppContext) {
public void contextInitialized(ServletContextEvent event) {
}
});
} else {
this.logger.debug("No ContextLoaderListener registered, as createRootApplicationContext() did not return an application context");
}
}
- createRootApplicationContext方法里调用一开始重写的configure方法,拿到SpringBoot主程序并启动
java
protected WebApplicationContext createRootApplicationContext(ServletContext servletContext) {
// 1、创建SpringApplicationBuilder
SpringApplicationBuilder builder = this.createSpringApplicationBuilder();
StandardServletEnvironment environment = new StandardServletEnvironment();
// 2、准备环境
environment.initPropertySources(servletContext, (ServletConfig)null);
builder.environment(environment);
builder.main(this.getClass());
ApplicationContext parent = this.getExistingRootWebApplicationContext(servletContext);
if (parent != null) {
this.logger.info("Root context already created (using as parent).");
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, (Object)null);
builder.initializers(new ApplicationContextInitializer[]{new ParentContextApplicationContextInitializer(parent)});
}
// 3、初始化
builder.initializers(new ApplicationContextInitializer[]{new ServletContextApplicationContextInitializer(servletContext)});
builder.contextClass(AnnotationConfigServletWebServerApplicationContext.class);
// 4、调用configure方法,子类重写了这个方法,将SpringBoot的主程序类传入了进来
builder = this.configure(builder);
SpringApplication application = builder.build();
if (application.getAllSources().isEmpty() && AnnotationUtils.findAnnotation(this.getClass(), Configuration.class) != null) {
application.addPrimarySources(Collections.singleton(this.getClass()));
}
Assert.state(!application.getAllSources().isEmpty(), "No SpringApplication sources have been defined. Either override the configure method or add an @Configuration annotation");
if (this.registerErrorPageFilter) {
application.addPrimarySources(Collections.singleton(ErrorPageFilterConfiguration.class));
}
// 5、启动Spring应用
return this.run(application);
}
}
// 此方法被启动引导类 ServletInitializer有方法重写, 传入的是应用构建器SpringApplicationBuilder, 也就是SpringBoot的主程序类
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return builder;
}
Q9、如何自定义一个Starter?
https://www.cnblogs.com/hello-shf/p/10864977.html
Q10、SpringBoot读取配置文件的原理是什么?加载顺序是怎样的?
答案:
通过事件监听的方式读取配置文件,同一个配置,优先级高的覆盖优先级低的,不同配置则取并集。