首页 > 其他分享 >SpringBoot面试题

SpringBoot面试题

时间:2023-06-23 20:44:28浏览次数:55  
标签:面试题 args SpringBoot jar 配置 boot mainClass

SpringBoot中常见的面试题:

1.SpringBoot中常用的注解有哪些:

对于理解SpringBoot的自动配置(自动装配)原理作出铺垫。

1.@SpringBootApplication:这个注解标识了SpringBoot的工程,这个注解标识了一个SpringBoot工程,它实际上是另外三个注解合成的。
2.@SpringBootConfiguration:这个注解实际就是一个@Configuration,表示启动类也是一个配置类
3.@EnableAutoConfiguration:向Spring容器中导入一个Selector,用来加载ClassPath下SpringFactories中所定义的自动配置类,将这些自动加载为配置的Bean
4.@ConditionalXXX也很关键:
    (1)@ConditionalOnBean
    (2)@ConditionalOnClass
    (3)@ConditionalOnExpression
    (4)@ConditionalOnMissingBean等等

2.SpringBoot中自动配置(自动装配)原理:

(1)pom.xml

  • spring-boot-dependencies:核心依赖在父工程当中!我们点击Spring-boot-starter-parent然后又会有一个spring-boot-dependencies,我们使用ctrl+鼠标左键进入往下拉会看到大量的jar包版本。

image-20211102095750486

  • 我们在写或者引入一些Springboot依赖的时候,不需要指定版本仓库,因为有这些版本的仓库,同时里面存在一系列的自动的资源过滤的配置文件。不需要像Spring在配置资源过滤文件。

(2)启动器:

 <!--启动器-->
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter</artifactId>
</dependency>
  • 启动器说白了就是springboot的启动场景:

    ​ 比如spring-boot-starter-web,他就会帮我们自动导入web环境所有的依赖!

  • springboot会将所有的功能场景变成一个个启动器(例如:Spring-boot-starter-web就会默认导入Tomcat的运行环境,不要再配置Tomcat

  • 如果我们要使用什么功能,就只需要找到其对应的启动器即可。在官网中可以找到starter的文件,说明文件中存在所有的需要的启动器的说明。

(3)主程序:

@SpringBootApplicationContext:标注这个类是一个SpringBoot的应用:

package com.kuang;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

//标注这个类是一个springboot的应用:
@SpringBootApplication
public class Springboot02HelloworldApplication {

	public static void main(String[] args) {
		//将springboot应用启动:反射加载该类的对象:
		SpringApplication.run(Springboot02HelloworldApplication.class, args);
	}
}
  • 注解:对于SpringBootApplication注解的深入探析:

    @SpringBootConfiguration:SpringBoot的配置
             @Configuration:spring配置类
             @Component:说明这也是一个spring的组件
    @EnableAutoConfiguration:自动导入配置
             @AutoConfigurationPackage:自动配置包
                     @Import({Registrar.class}):自动配置包注册
             @Import({AutoConfigurationImportSelector.class}):自动配置导入选择:
               注意AutoConfigurationImportSelector实现了DeferredImportSelector--
    
  • 源码详细的解析:

AutoConfigurationImportSelector-------->getAutoConfigurationEntry
-------->getCandidateConfigurations-------->loadFactoryNames---->

image-20230623203158360

1)首先介绍getSpringFactoriesLoaderFactoryClass()函数:

就是标注了EnableAutoConfiguation的这个类:

我们@SpringBootApplication就标注了它

image-20230623203221895

因此可以知道兜兜转转的目的就是将启动类下的所有资源进行导入。

2)接下来在getCandidateConfigurations中还有一个方法Assert.notEmpty()方法:

再点击进入loadFactoryNames方法当中:(该类在SpringFactoriesLoader类当中

image-20230623203236800

META-INF/spring:自动配置的核心文件:

image-20230623203246784

会发现下面有众多的包文件配置:

image-20230623161534898

选取一个WebMvcAutoConfiguration进入查看:

image-20230623161747353

结论:

​ springboot所有的自动配置都是在启动类中被扫描并且加载。扫描META-INF/factories,所有的自动配置类都在这里,但不一定生效,要判断条件是否成立,

主要到导入了对应的starter,就有了对应的启动器了,有了启动器,我们自动装配就会生效,然后就配置成功。

image-20230623164229460

3. 为什么springBoot的jar可以直接运行:

1.SpringBoot提供一个插件spring-boot-maven-plugin用于把程序打包成一个可执行的jar包。

<build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
</build>

但是该jar包与一般的Java项目下的jar包并不同,此jar包可以直接用:

java-jar xxxx.jar命令直接运行并部署

2.查询原因:
SpringBoot应用打包成为jar包之后,生成的jar包是带有普通jar包文件的特殊的jar包,主要包含了应用程序依赖的jar包与SpringBoot loader相关的类。

我们可以解压形成的SpringBoot的jar包,在Boot-INF\lib目录下就有该项目代码依赖的jar包文件。

3.底层的原理:

在Spring Boot项目的jar中会生成一个MANIFEST.MF文件(路径:META-INF\MANIFEST.MF),打开该文件你会看到有一个MainClass的映射,其对应的值是一个类,就是执行‘java -jar’命令后正式执行的类,mainclass类是springboot插件引入后自动添加的。

image-20230623180830088

如果想看JarLauncher类的源码需要手动引入‘spring-boot-loader’依赖,其实声明‘spring-boot-maven-plugin’插件也会自动引入其依赖,但不能查看到源码内容:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-loader</artifactId>
</dependency>

打开之后可以看到此类有一个主程序入口,main方法

image-20230623180933773

以下就是源码解析部分:

protected void launch(String[] args) throws Exception {
   JarFile.registerUrlProtocolHandler();
   //自定义类加载器加载jar文件
   ClassLoader classLoader = createClassLoader(getClassPathArchives());
   //关注getMainClass方法
   launch(args, getMainClass(), classLoader);
}

此处我们先去看下getMainClass方法,关键点在于getMainClass()方法,获取META-INF\MANIFEST.MF文件键为‘Start-Class’的值(类),并进行调用:(有两种方式)

image-20230623181110060

第一种:ExecutableArchiveLauncher实现方式:

@Override
protected String getMainClass() throws Exception {
   Manifest manifest = this.archive.getManifest();
   String mainClass = null;
   if (manifest != null) {
      mainClass = manifest.getMainAttributes().getValue("Start-Class");
   }
   if (mainClass == null) {
      throw new IllegalStateException("No 'Start-Class' manifest entry specified in " + this);
   }
   return mainClass;
}

第二种:PropertiesLauncher实现方式:

@Override
protected String getMainClass() throws Exception {
   String mainClass = getProperty(MAIN, "Start-Class");
   if (mainClass == null) {
      throw new IllegalStateException("No '" + MAIN + "' or 'Start-Class' specified");
   }
   return mainClass;
}

我们再回头去看一下launch方法:

/**
 * mainClass参数就是前面getMainClass从MANIFEST.MF文件中获取到的Start-Class
 */
protected void launch(String[] args, String mainClass, ClassLoader classLoader) throws Exception {
   Thread.currentThread().setContextClassLoader(classLoader);
   createMainMethodRunner(mainClass, args, classLoader).run(); //注意此处run方法调用!!
}

我们会发下其中就是调用的配置的主启动类的run方法:

protected MainMethodRunner createMainMethodRunner(String mainClass, String[] args, ClassLoader classLoader) {
   return new MainMethodRunner(mainClass, args);
}

最后是new了一个MainMethodRunner类,然后在createMainMethodRunner调用run方法,而在run方法中可以看到是通过反射去执行Start-Class对于类的main方法,开始执行业务逻辑:

public class MainMethodRunner {

   private final String mainClassName;

   private final String[] args;

   /**
    * Create a new {@link MainMethodRunner} instance.
    * @param mainClass the main class
    * @param args incoming arguments
    */
   public MainMethodRunner(String mainClass, String[] args) {
      this.mainClassName = mainClass;
      this.args = (args != null) ? args.clone() : null;
   }

   public void run() throws Exception {
      Class<?> mainClass = Thread.currentThread().getContextClassLoader().loadClass(this.mainClassName);
      Method mainMethod = mainClass.getDeclaredMethod("main", String[].class);
      mainMethod.invoke(null, new Object[] { this.args });
   }
}

总结:面试时候按照如下的回答就可以:

1.源码的执行流程包括如下:
   SpringBoot打包成jar包,会形成一个Fat jar的jar包文件-------->当我们运行java -jar 文件之后会首先找到Main-class配置信息
   也就是Jarlauncher类-------->调用launch方法------>然后会调用getMainClass方法(实现的方式主要有两种)---->
   主要从Manifest配置清单当中拿到start-class配置的信息(也就是我们的主启动类)------>进入launch方法会调用 
   createMainMethodRunner----->会首先创建MainMethodRunner对象----->使用MainMethodRunner执行run()方法(利用反射机制)
2.主要流程:
  (1)SpringBoot提供一个插件Spring-boot-maven-plugin用于把程序打包成一个可执行的jar包文件
  (2)SpringBoot应用打包成功后会形成一个Fat jar,包含了应用依赖的jar和SpringBoot loader相关的类
  (3)java-jar会去manifest清单文件中找真正的主启动类(Main-class)
  (4)Fat jar的启动Main函数是Jarlauncher,它负责创建一个新的线程来寻找项目的主启动类也就是start-class配置的信息,并通过
   MainMethodRunner创建新对象并调用项目主启动类的run方法运行项目。

4. SpringBoot的启动流程:

SpingBoot的启动流程也可以通俗的说成主启动类的run方法调用后发生了什么事情?

image-20230623184856521

注意:

1.new SpringApplication所做的事情:

image-20230623183725994

上面的图片也就是SpringBoot启动后的初始化的阶段:

(1)从Spring.factories中读取了ApplicationListener监听器
(2)从Spring.factories中读取了ApplicationInitializer初始化器
(3)同时将main方法所在的类放入mainApplicationClass当中

2.run()方法所做的事情:
最最主要的事情还是加载我们IOC容器进行初始化:

image-20230623185044018

包括这一步还做了一下的内容:

image-20230623190049054

5. SpringBoot内置的Tomcat启动原理:

1.注意SpringBoot我们需要添加web的场景启动器。就是xml中的web依赖:

 <!--启动器-->
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter</artifactId>
</dependency>

注意SpringBoot会在其中添加ServletWebServerFactoryAutoConfiguration servlet的容器自动配置类,会决定使用哪一个web容器,例如下面:

image-20230623203328989

  • 该自动配置类通过@Import导入可用(可用@ConditionOnClass)判断决定使用哪一个web容器工厂(默认是Tomcat)
  • 在内嵌Tomcat类中配置一个TomcatServletWebServerFactory的Bean工厂
  • 他会在SpringBoot容器启动时加载ioc容器refresh 在onrefresh创建内嵌的Tomcat启动。

详细流程:

1.在自动配置类当中启用内嵌的Tomcat,配置一个Tomcat的配置工厂

image-20230623201230634

2.SpringApplication.run方法运行后会创建一个SpringBoot的容器叫做AnnotationConfigServletWebServerApplicationContext主要采用的是调用createApplicationContext方法调用进行创建:

image-20230623201558077

3.调用容器的refreshContext方法,加载IOC容器,底层就会调用Spring源码中最熟悉的源码方法refresh方法:

image-20230623201653039

4.解析自动配置类:

invokeBeanFactoryPostProcessor方法解析@Bean等注解,我们会发现第一步配置的TomcatServletWebServerFactory工厂上也使用@Bean注解,因此也会解析

5.在invokeBeanFactoryPostProcessor方法调用完毕后会调用onfresh方法,会调用ServletWebServerApplicationContext中的createWebServer方法,注意ServletWebServerApplicationContext为AnnotationConfigServletWebServerApplicationContext的父类,在createWebServer方法中会调用getWebServerFctory方法:

image-20230623202143622

ServletWebServerFactory的实现类:

image-20230623202204221

6.调用getWebServer方法:
image-20230623202325872

涉及到Tomcat创建的关键代码如下:

image-20230623202408473

调用Tomcat在getTomcatWebServer---->TomcatWebServer------>initialize():

this.tomcat.start();

image-20230623202552206

Tomcat挂起等待被调用:

startDaemonAwaitThread()

image-20230623202649616

标签:面试题,args,SpringBoot,jar,配置,boot,mainClass
From: https://www.cnblogs.com/liuyuchengcj/p/17500150.html

相关文章

  • 2023年最新5000道校招常用编程面试题分享(附详细题解)
    截止到2021年最新,本资源整理了近5000道校招常用面试题,并附带详细的解题思路及代码,包含leetcode,校招笔试题,面试题,算法题,语法题。持续更新中。。。目录内容截图......
  • C/C++经典面试题1(精心整理,附参考答案)
    1.说一下static关键字的作用2.说一下C++和C的区别(1)设计思想上(2)语法上3.说一说c++中四种cast转换(1)const_cast(2)static_cast(3)dynamic_cast(4)reinterpret_cast(5)为什么不使用C的强制转换?4.请说一下C/C++中指针和引用的区别?5.给定三角形ABC和一点P(x,y,......
  • 2021最新C++面试题(附答案)
    今天分享给大家的是比较全面的C/C++面试题,也都是C++版本升级之后,重新整理归纳的最新答案,会让C++面试者少走很多不必要的弯路。同时每个C++面试题都尽量做到了详尽的面试解析文档,以确保每个阶段的读者都能看得懂,同时这部分C++面试文档也是可以免费的提供给有需要的同学们学习的!博......
  • c++面试题(亲测常问)
    注意:此题为我自己面试被问到的,及一些摘抄的,如有侵权请联系我马上删除!1. 2.32位指针地址所占字节数为四举例说明:char*p;chartest[10];p=test;sizeof(p)=4(32位系统)//实质是求指针类型所占字节数,32位对应4字节,64位对应8字节sizeof(*p)=1//实质是求指针所指的内容所在......
  • C++面试题汇总
    目录1、C++三大特性1.1封装1.2继承1.3多态2、C++中map与unordered_map的区别3、unordered系列关联式容器4、STL常用函数,容器和使用容器的方法5、map的底层实现,存储的是什么,实现的时间复杂度6、虚函数6.1什么是虚函数6.2虚函数和纯虚函数的区别7、C++set和map......
  • springboot整合mysql和clickhouse多数据源
    1、添加依赖<!--MyBatis-PlusStarter--><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.2.0</version></dependency>......
  • 基于SpringBoot实现SSMP整合的案例源码
    案例介绍:基于SpringBoot实现SSMP整合的案例之一(案例分析与模块创建)-掘金(juejin.cn)源码下载:点我......
  • springboot & mongodb test
    <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-mongodb</artifactId></dependency>下载方式https://www.mongodb.com/docs/manual/tutorial/install-mongodb-on-os-x/docker方式do......
  • C++面试题 --imxiangzi 看看
    目录语言基础类0.各种类型和0值比较1.指针和引用的区别?2.static和const的用法,(能说出越多越好)(重点)3.externc 作用4.堆和栈的区别6. 头文件中的ifndef/define/endif 干什么用?7. 用struct与class的区别8.派生类与虚函数概述9. 虚函数与纯虚函数区别10.深拷贝与......
  • 原创 C++的校招的面试题,看看你能答对几个?
    嗨~大家好呀,最近后台有人问小谷,C++校招的话,需要了解哪些内容,大家知道的,小谷有求必应的,那么之后我就来周期性更新一下作为一名C++开发工程师要掌握的知识,本期主要介绍一下C++基础知识吧!1、面向对象的三大特性:封装、继承、多态封装:就是把客观事物封装成抽象的类,可以使某个属性只......