Spring Boot面试题

目录

什么是 Spring Boot?

Spring Boot 有哪些优点?

Spring Boot的启动流程

Spring Boot Starter是什么?如何自定义Spring Boot Starter

什么是 JavaConfig?

spring boot 核心配置文件是什么?bootstrap.properties 和 application.properties 有何区别 ?

什么是 Spring Profiles?

spring-boot-starter-parent 有什么用 ?

Spring Boot 打成的 jar 和普通的 jar 有什么区别 ?

Spring Boot 中如何解决跨域问题 ?


什么是 Spring Boot?

Spring Boot 是 Spring 开源组织下的子项目,是 Spring 组件一站式解决方案,主要是简化了使用 Spring 的难度,简化了繁重的配置,提供了各种启动器,开发者能快速上手。

Spring Boot 有哪些优点?

Spring Boot 主要有如下优点:

  1. 容易上手,提升开发效率,为 Spring 开发提供一个更快、更广泛的入门体验。

  2. 开箱即用,远离繁琐的配置。

  3. 没有代码生成,也不需要XML配置。

  4. 提供了一系列大型项目通用的非业务性功能,例如:内嵌服务器、安全管理、运行数据监控、运行状况检查和外部化配置等。

  5. 避免大量的 Maven 导入和各种版本冲突。

Spring Boot的启动流程

Spring Boot 入口——main方法

@SpringBootApplication
public class Application {
   public static void main(String[] args) throws Exception {
       SpringApplication.run(Application.class, args);
  }
}

从上面代码可以看出,Annotation定义(@SpringBootApplication)和类定义(SpringApplication.run)最为耀眼,所以分析 Spring Boot 启动过程,我们就从这两方面开始。

从源码声明可以看出,@SpringBootApplication相当于 @SpringBootConfiguration + @ComponentScan +  @EnableAutoConfiguration ,因此我们直接拆开来分析。

上面三个注解都在做一件事:注册bean到spring容器。他们通过不同的条件不同的方式来完成:

  • @SpringBootConfiguration 通过与 @Bean 结合完成Bean的 JavaConfig 配置;

  • @ComponentScan 通过范围扫描的方式,扫描特定注解注释的类,将其注册到Spring容器;

  • @EnableAutoConfiguration 通过 spring.factories 的配置,并结合 @Condition 条件,完成bean的注册;

除了上面的三个注解,还可以使用@Import注解将bean注册到Spring容器

  • @Import 通过导入的方式,将指定的class注册解析到Spring容器;

Spring Boot 启动流程

SpringApplication的实例化

  • 推断应用类型是否是Web环境

  • 设置初始化器(Initializer)

  • 设置监听器(Listener)

  • 推断应用入口类(Main)

SpringApplication.run方法

  • 获取SpringApplicationRunListeners

  • 准备配置环境ConfigurableEnvironment

  • 创建ApplicationContext应用上下文

  • ApplicationContext前置处理

  • ApplicationContext刷新

  • ApplicationContext后置处理

完成了实例化,下面开始调用run方法

// 运行run方法
public ConfigurableApplicationContext run(String... args) {
   // 此类通常用于监控开发过程中的性能,而不是生产应用程序的一部分。
   StopWatch stopWatch = new StopWatch();
   stopWatch.start();

   ConfigurableApplicationContext context = null;
   Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();

   // 设置java.awt.headless系统属性,默认为true
   // Headless模式是系统的一种配置模式。在该模式下,系统缺少了显示设备、键盘或鼠标。
   configureHeadlessProperty();

   // KEY 1 - 获取SpringApplicationRunListeners
   SpringApplicationRunListeners listeners = getRunListeners(args);

   // 通知监听者,开始启动
   listeners.starting();
   try {
       ApplicationArguments applicationArguments = new DefaultApplicationArguments(
               args);

       // KEY 2 - 根据SpringApplicationRunListeners以及参数来准备环境
       ConfigurableEnvironment environment = prepareEnvironment(listeners,
               applicationArguments);
       
       configureIgnoreBeanInfo(environment);

       // 准备Banner打印器 - 就是启动Spring Boot的时候打印在console上的ASCII艺术字体
       Banner printedBanner = printBanner(environment);

       // KEY 3 - 创建Spring上下文
       context = createApplicationContext();

       // 注册异常分析器
       analyzers = new FailureAnalyzers(context);

       // KEY 4 - Spring上下文前置处理
       prepareContext(context, environment, listeners, applicationArguments,
               printedBanner);

       // KEY 5 - Spring上下文刷新
       refreshContext(context);

       // KEY 6 - Spring上下文后置处理
       afterRefresh(context, applicationArguments);

       // 发出结束执行的事件
       listeners.finished(context, null);

       stopWatch.stop();
       if (this.logStartupInfo) {
           new StartupInfoLogger(this.mainApplicationClass)
                  .logStarted(getApplicationLog(), stopWatch);
      }
       return context;
  }
   catch (Throwable ex) {
       handleRunFailure(context, listeners, exceptionReporters, ex);
       throw new IllegalStateException(ex);
  }
}

Spring Boot Starter是什么?如何自定义Spring Boot Starter

Spring boot之所以流行,很大原因是因为有Spring Boot Starter。Spring Boot Starter是Spring boot的核心,可以理解为一个可拔插式的插件,例如,你想使用Reids插件,那么可以导入spring-boot-starter-redis依赖

Starter的命名

官方对Starter项目的jar包定义的 artifactId 是有要求的,当然也可以不遵守。Spring官方Starter通常命名为spring-boot-starter-{name}如:spring-boot-starter-web,Spring官方建议非官方的starter命名应遵守{name}-spring-boot-starter的格式。

传统的做法

在没有starter之前,假如我想要在Spring中使用jpa,那我可能需要做以下操作:

  1. 在Maven中引入使用的数据库的依赖(即JDBC的jar)

  2. 引入jpa的依赖

  3. 在xxx.xml中配置一些属性信息

  4. 反复的调试直到可以正常运行

需要注意的是,这里操作在我们每次新建一个需要用到jpa的项目都需要重复的做一次

使用Spring Boot Starter可以提升效率

starter的主要目的就是为了解决上面的这些问题。

使用starter的好处,starter的作用:

  • 帮助用户去除了繁琐的重复性的构建操作

  • 在“约定大于配置”的理念下,ConfigurationProperties还帮助用户减少了无谓的配置操作

  • 因为 application.properties 文件的存在,用户可以集中管理自定义配置

创建自己的Spring Boot Starter

如果你想要自己创建一个starter,那么基本上包含以下几步

  1. 新建一个Maven项目,在pom.xml文件中定义好所需依赖;

  2. 新建配置类,写好配置项和默认值,使用@ConfigurationProperties指明配置项前缀;

  3. 新建自动装配类,使用@Configuration@Bean来进行自动装配;

  4. 新建spring.factories文件,用于指定自动装配类的路径;

  5. 将starter安装到maven仓库,让其他项目能够引用

spring.factories文件位于resources/META-INF目录下,需要手动创建;org.springframework.boot.autoconfigure.EnableAutoConfiguration后面的类名说明了自动装配类,如果有多个 ,则用逗号分开;使用者应用(SpringBoot)在启动的时候,会通过org.springframework.core.io.support.SpringFactoriesLoader读取classpath下每个Starter的spring.factories文件,加载自动装配类进行Bean的自动装配;

什么是 JavaConfig?

Spring JavaConfig 是 Spring 社区的产品,它提供了使用注释来配置Bean的纯 Java 方法。因此它有助于避免使用 XML 配置。使用 JavaConfig 的优点在于:

(1)面向对象的配置。由于配置被定义为 JavaConfig 中的类,因此用户可以充分利用 Java 中的面向对象功能。一个配置类可以继承另一个,重写它的@Bean 方法等。

(2)减少或消除 XML 配置。基于依赖注入原则的外部配置的好处已被证明。但是,许多开发人员不希望在 XML 和 Java 之间来回切换。JavaConfig 为开发人员提供了一种纯 Java 方法来配置与 XML 配置概念相似的 Spring 容器。从技术角度来讲,只使用 JavaConfig 配置类来配置容器是可行的,但实际上很多人认为将JavaConfig 与 XML 混合匹配是理想的。

(3)类型安全和重构友好。JavaConfig 提供了一种类型安全的方法来配置 Spring容器。由于 Java 5.0 对泛型的支持,现在可以按类型而不是按名称检索 bean,不需要任何强制转换或基于字符串的查找。

spring boot 核心配置文件是什么?bootstrap.properties 和 application.properties 有何区别 ?

单纯做 Spring Boot 开发,不太容易遇到 bootstrap.properties 配置文件,但是在结合 Spring Cloud 时,这个配置就会经常遇到了,特别是在需要加载一些远程配置文件的时侯。

spring boot 核心的两个配置文件:

  • bootstrap (. yml 或者 . properties):bootstrap 由父 ApplicationContext 加载的,比 applicaton 优先加载,配置在应用程序上下文的引导阶段生效。一般来说我们在 Spring Cloud Config 或者 Nacos 中会用到它。且 bootstrap 里面的属性不能被覆盖;

  • application (. yml 或者 . properties):由ApplicatonContext 加载,用于 spring boot 项目的自动化配置。

什么是 Spring Profiles?

Spring Profiles 主要有下面两个使用场景:

  • 根据不同的使用环境定义不同的profiles文件,比如开发,测试和生产,可以大大省去我们修改配置信息而带来的烦恼

  • 根据不同的profiles(dev,test,prod)注册不同的bean,当应用程序在开发中运行时,只有某些 bean 可以加载,而在生产中,某些其他 bean 可以加载

spring-boot-starter-parent 有什么用 ?

我们都知道,新创建一个 Spring Boot 项目,默认都是有 parent 的,这个 parent 就是 spring-boot-starter-parent ,spring-boot-starter-parent 主要有如下作用:

  1. 定义了 Java 编译版本为 1.8 。

  2. 使用 UTF-8 格式编码。

  3. 继承自 spring-boot-dependencies,这个里边定义了依赖的版本,也正是因为继承了这个依赖,所以我们在写依赖时才不需要写版本号。

  4. 执行打包操作的配置。

  5. 自动化的资源过滤。

  6. 自动化的插件配置。

  7. 针对 application.properties 和 application.yml 的资源过滤,包括通过 profile 定义的不同环境的配置文件,例如 application-dev.properties 和 application-dev.yml。

Spring Boot 打成的 jar 和普通的 jar 有什么区别 ?

Spring Boot 项目最终打包成的 jar 是可执行 jar ,这种 jar 可以直接通过 java -jar xxx.jar 命令来运行,这种 jar 不可以作为普通的 jar 被其他项目依赖,即使依赖了也无法使用其中的类。

Spring Boot 的 jar 无法被其他项目依赖,主要还是他和普通 jar 的结构不同。普通的 jar 包,解压后直接就是包名,包里就是我们的代码,而 Spring Boot 打包成的可执行 jar 解压后,在 \BOOT-INF\classes 目录下才是我们的代码,因此无法被直接引用。如果非要引用,可以在 pom.xml 文件中增加配置,将 Spring Boot 项目打包成两个 jar ,一个可执行,一个可引用。

Spring Boot 中如何解决跨域问题 ?

跨域可以在前端通过 JSONP 来解决,但是 JSONP 只可以发送 GET 请求,无法发送其他类型的请求,在 RESTful 风格的应用中,就显得非常鸡肋,因此我们推荐在后端通过 (CORS,Cross-origin resource sharing) 来解决跨域问题。这种解决方案并非 Spring Boot 特有的,在传统的 SSM 框架中,就可以通过 CORS 来解决跨域问题,只不过之前我们是在 XML 文件中配置 CORS ,现在可以通过实现WebMvcConfigurer接口然后重写addCorsMappings方法解决跨域问题。

@Configuration
public class CorsConfig implements WebMvcConfigurer {

   @Override
   public void addCorsMappings(CorsRegistry registry) {
       registry.addMapping("/**")
              .allowedOrigins("*")
              .allowCredentials(true)
              .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
              .maxAge(3600);
  }

}

使用上面的方式解决跨域会有点问题,具体描述如下

我们使用cookie存放用户登录的信息,在spring拦截器进行权限控制,当权限不符合时,直接返回给用户固定的json结果。

当用户登录以后,正常使用;当用户退出登录状态时或者token过期时,由于拦截器和跨域的顺序有问题,出现了跨域的现象。

我们知道一个http请求,先走filter,到达servlet后才进行拦截器的处理,如果我们把cors放在filter里,就可以优先于权限拦截器执行。

@Configuration
public class CorsConfig {

   @Bean
   public CorsFilter corsFilter() {
       CorsConfiguration corsConfiguration = new CorsConfiguration();
       corsConfiguration.addAllowedOrigin("*");
       corsConfiguration.addAllowedHeader("*");
       corsConfiguration.addAllowedMethod("*");
       corsConfiguration.setAllowCredentials(true);
       UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource();
       urlBasedCorsConfigurationSource.registerCorsConfiguration("/**", corsConfiguration);
       return new CorsFilter(urlBasedCorsConfigurationSource);
  }

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值