Spring&SpringBoot常见面试题

160 阅读12分钟

谈谈Spring IOC

  • ioc 控制反转,是一种设计思想,而不是具体实现
  • 从原来应用代码创建对象,交由spring容器来管理对象的创建和管理,这样应用程序只需要关注业务逻辑便可。
  • ioc使得对象的耦合度降低

IOC和DI有区别吗

  • IOC最常见以及最合理的实现方式就是DI依赖注入: 将对象的依赖关系注入到对象内部来实现控制反转。
  • 常见方式:
    • setter: 通过类的setter方法来注入依赖项。
    • 依赖field字段注入:使用@Autowried或@Resources依赖注入依赖项。
    • 构造器注入:使用类的构造器来注入,推荐,避免空指针异常。

什么是SpringBean,作用域有哪些?

  • Bean就指那些被ioc容器所管理的对象

  • 作用域:

    • singleton: Ioc容器中唯一实例,对应的bean都是单例的,对应单例设计模式。
    • propotype: 每次获取都会创建一个新的bean实例。
    • request (仅 Web 应用可用): 每一次 HTTP 请求都会产生一个新的 bean(请求 bean),该 bean 仅在当前 HTTP request 内有效。
    • session (仅 Web 应用可用) : 每一次来自新 session 的 HTTP 请求都会产生一个新的 bean(会话 bean),该 bean 仅在当前 HTTP session 内有效。
    • application/global-session (仅 Web 应用可用):每个 Web 应用在启动时创建一个 Bean(应用 Bean),该 bean 仅在当前应用启动时间内有效。
    • websocket (仅 Web 应用可用):每一次 WebSocket 会话产生一个新的 bean。

bean是线程安全的吗

  • 取决于作用域和状态:
    • propotype,每次获取都会创建新bean实例,不会有线程安全问题
    • 几乎所有场景默认使用singleton, 如果bean有状态就会出现线程安全问题。有状态:类中包含可变的成员变量
    • 解决方案:
      • 尽量设计无状态的bean
      • 使用ThreadLocal,将可变成员变量存在ThreadLocal中,保证线程独立。
      • 利用Synchonrized 或 ReentrantLock 来保证线程安全。

Spring Bean的生命周期?

  • 整体上:实例化-> 属性赋值 -> 初始化 -> 销毁
  • 其中初始化步骤包含:Aware接口的依赖注入、BeanPostProcessor初始化前后的的处理、InitializingBean和init-method的初始化操作。

image.png

  • 项目结合bean生命周期的操作

BeanDefinition的作用? 懒加载是什么?

  • BeanDefinition 用来描述和封装一个bean的元信息的概念。包含了Spring容器再创建Bean对象所需要的全部信息,比如:Bean的Class类型,作用域,懒加载(Lazy Initialization)等。
  • 懒加载:决定Bean是否应当延迟加载,如果设置了懒加载,Bean只会在第一次请求时创建实例,而不是容器启动时立即创建。

Spring的AOP有什么用?你项目中哪里用到了AOP?

  • AOP:面向切面编程,能够将与业务无关,却为业务模块所共同调用的逻辑和责任封装起来。(如日志管理、事务管理、权限管理等)减少系统的重复代码,降低模块耦合,提高系统的可拓展性和可维护性。

  • 类型

    • Before(前置通知):目标对象的方法调用之前触发
    • After (后置通知):目标对象的方法调用之后触发
    • AfterReturning(返回通知):目标对象的方法调用完成,在返回结果值之后触发
    • AfterThrowing(异常通知):目标对象的方法运行中抛出 / 触发异常后触发。AfterReturning 和 AfterThrowing 两者互斥。如果方法调用成功无异常,则会有返回值;如果方法抛出了异常,则不会有返回值。
    • Around (环绕通知):编程式控制目标对象的方法调用。环绕通知是所有通知类型中可操作范围最大的一种,因为它可以直接拿到目标对象,以及要执行的方法,所以环绕通知可以任意的在目标对象的方法调用前后搞事,甚至不调用目标对象的方法
  • 常见的:

    • 基于AOP实现统一日志管理
    • 基于Redssion+AOP实现接口防刷,一个注解即可实现限制单个用户访问接口的次数
    • Spring Security 的@PreAuthor,也是基于AOP实现权限控制的。
    • 基于@Transtional实现事务管理。

Spring事务管理了解吗? @Transcational有什么用? 项目中时如何使用的?

  • 事务:逻辑上一组的操作,要么都执行,要么都不执行

  • ACID: 只有保证了原子性、隔离性、持久性才能保证一致性,也就是AID都是手段, C是目的。

    • 原子性Atomicity):事务是最小的执行单位,不允许分割。事务的原子性确保动作要么全部完成,要么完全不起作用;
    • 一致性Consistency):执行事务前后,数据保持一致,例如转账业务中,无论事务是否成功,转账者和收款人的总额应该是不变的;
    • 隔离性Isolation):并发访问数据库时,一个用户的事务不被其他事务所干扰,各并发事务之间数据库是独立的;
    • 持久性Durability):一个事务被提交之后。它对数据库中数据的改变是持久的,即使数据库发生故障也不应该对其有任何影响。
  • Spring支持两种事务管理:

    • 编程式事务管理:通过TranscationTemplate和TransactionManager管理事务,实际很少使用。
    • 声明式事务管理: 代码侵入性最小,推荐使用,通过AOP实现(基于@Transactional全注解的方式使用最多)
  • Spring事务管理最重要的三个接口:

    • PlantformTransactionManager: 事务管理器,Spring事务管理的核心

      • 其他平台实现此接口,例如JDBC Hibernate
      • 定义了3个方法:
        • getTransaction(): 获取事务
        • commit() 提交
        • rollback() 回滚
    • TransactionDefinition: 事务定义信息(隔离级别,传播行为,超时,只读,回滚规则)

      • 事务的传播行为:

        • PROPAGATION_REQUIRED: 如果当前存在事务,就加入该事务;如果当前不存在事务,就创建新事务。
        • PROPAGATION_REQUIRED_NEW: 创建一个新事务,如果已存在事务,就将此事务挂起
        • PROPAGATION_NESTED: 如果当前存在事务,就创建一个事务作为当前事务的子事务执行。 父事务提交,子事务提交;父事务回滚,子事务回滚;子事务回滚,可以不引发父事务回滚。、
        • PROPAGATION_MANDATORY:如果当前存在事务,则加入该事务;如果不存在,就抛出异常。
        • TransactionDefinition.PROPAGATION_SUPPORTS: 如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
        • TransactionDefinition.PROPAGATION_NOT_SUPPORTED: 以非事务方式运行,如果当前存在事务,则把当前事务挂起。
        • TransactionDefinition.PROPAGATION_NEVER: 以非事务方式运行,如果当前存在事务,则抛出异常。
    • TransactionStatus:事务运行的状态。

  • @Transactional的常用参数

属性名说明
Propagation事务的传播行为,默认REQUIRED
isloation事务的隔离级别,默认DEFAULT
timeout事务超时,默认-1(不会超时),如果超过时间限制还没有执行完就自动回滚事务
readOnly事务是否为只读事务,默认为false
rollbackFor指定触发回滚的异常类型,可以指定多个异常类型

Spring的循环依赖了解吗?怎么解决?

  • 循环依赖:是Bean对象循环引用,两个或者多个对象互相持有对方的引用。
  • 创建Bean对象的三级缓存:
    • 先去一级缓存SingletonObject获取,存在就返回
    • 如果不存在或对象正在创建,则去二级缓存earlySingletonObject获取
    • 如果还没获取到,去三级缓存SingletonFactories获取,通过执行objectFactorygetFactory()生成原始的Bean对象或者代理对象,获取成功后,从三级缓存中删除,放到二级缓存中
  • 解决方案: 从三级缓存 SingletonFactories中获取执行objectFactory的getObject方法,获取此循环依赖对象的前期暴漏对象(虽然没有初始化完成,但是可以拿到对象在堆中的存储地址了),并且将这个前期暴露对象放入二级缓存,这样就解决了循环依赖,不会重复初始化了。
  • Spring2.6.x之前允许循环依赖这种写法,即使出现也不会报错; Spring2.6.x之后默认不允许循环依赖,官方也不推荐了。

@Lazy能解决循环依赖吗?

  • @Lazy标识的标识延迟加载/懒加载,可以作用在类上、方法上、构造器上、方法参数上、成员变量上。
  • 懒加载一定程度上打破了循环依赖链,允许Spring容器顺利完成Bean的创建和注入。但不是根本上解决此问题,在构造器注入、更复杂场景中,懒加载并不能有效解决此问题。
  • 最佳实践应该是避免循环依赖。

SpringMVC包含那些组件?当收到请求的流程是什么样的?

  • DispatcherServlet核心的中央处理器,负责接收请求、分发,并给予客户端响应。
  • HandlerMapping处理器映射器,根据 URL 去匹配查找能处理的 Handler ,并会将请求涉及到的拦截器和 Handler 一起封装。
  • HandlerAdapter处理器适配器,根据 HandlerMapping 找到的 Handler ,适配执行对应的 Handler
  • Handler请求处理器,处理实际请求的处理器。
  • ViewResolver视图解析器,根据 Handler 返回的逻辑视图 / 视图,解析并渲染真正的视图,并传递给 DispatcherServlet 响应客户端

image.png

  • 流程:
    • 客户端发送请求,DispatcherServlet拦截请求
    • DispatcherSerlvet根据请求调用HandlerMapping,根据URL查找能够处理的Handler
    • DispatcherServlet根据HandlerAdapter适配器执行Handler
    • Handler完成对用户的请求处理后,返回一个ModelAndView(模型和视图信息)对象给DispatcherSerlvet
    • DispatcherServlet调用ViewResolver视图解析器解析view
    • DispatcherServlet将的得到的Model交给View进行视图渲染
    • 最后把View返回给客户端。

Spring\SpringMVC SpringBoot的区别?

  • SpringMVC是spring的一个重要模块,主要还是赋予Spring快速构建MVC架构的Web程序。
  • SpringBoot目的是简化Spring的开发(减少配置文件,开箱即用)

SpringBoot自动配置是如何实现的?

  • SpringBoot的自动配置机制 通过 @EnableAutoConfiguration 启动。该注解利用 @Import 注解导入了 AutoConfigurationImportSelector类,而 AutoConfigurationImportSelector 了负责加载并管理所有的自动配置类。这些自动配置类通常在 META-INF/spring.factories文件中声明,并根据项目的依赖和配置条件,通过条件注解(如 @ConditionalOnClass @ConditionalOnBean等)判断是否应该生效。

如何使用Spring Boot实现全局异常处理

  • 可以借助 @RestControllerAdvice@Exceptionhandler实现全局异常统一处理。
@RestControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler(BusinessException.class)
    public Result businessExceptionHandler(HttpServletRequest request, BusinessException e){
        ...
        return Result.faild(e.getCode(), e.getMessage());
    }
}
  • @RestControllerAdvice 是ControllerAdvice和@ResponseBody的结合体。

Spring的缺点

  • Spring提供了 依赖注入和面向切面编程
  • 虽然 组件代码 是轻量级的,但是配置重(需要大量的xml配置)
  • 虽然 Spring2.5 引入了基于注解的 组件扫描,消除了 大量 基于针对应用程序自身组件的显式XML配置; Spring 3.0 引入基于Java的配置,这是一种安全的可重构配置方式 ,可以代替xml配置。

为什么要求springBoot?或者说 作用?

  • 为了简化Spring的 开发部署过程,使得能够 专注 业务的逻辑的开发。

SpringBoot的优势:

  • 提高生产力:SpringBoot的自动配置和开箱即用的功能,显著减少了手动配置和 样板代码的 编写 时间,使得开发者能够更快速地 构建和交付应用程序。
  • 与 Spring生态系统的无缝衔接。
  • 减少手动配置:提供了合理 的默认配置,大多数情况可以直接使用默认配置来去启动项目 。
  • 嵌入式服务器:SpringBoot自带内嵌的 HTTP服务器 (tomcat,jetty)
  • 适合微服务 架构: SpringBoot使得每个微服务都可以独立运行和配置。简化了微服务的开发、测试和运维 工作。
  • 内置多插件:内置maven和gradle等工具。

SpringBoot Starters

  • Spring Boot Starters是一组便捷的依赖描述符,它们预先打包了常用的库和配置。当我们开发Spring应用时,只需要添加一个Starter依赖项,即可自动引入所有必要的库和配置,而无需手动逐一添加和配置相关依赖。
  • 常用的starter组件

    • spring-boot-starter:基础的 Starter,包含了启动 Spring 应用所需的核心依赖,如 Spring 框架本身和日志系统(默认使用 SLF4J 和 Logback)。
    • spring-boot-starter-web:用于构建 Web 应用程序,包括 RESTful 服务。它包含了 Spring MVC、Tomcat(默认嵌入式服务器)、Jackson 等依赖。
    • spring-boot-starter-data-jpa:用于构建 JPA 应用程序,包含 Spring Data JPA 和 Hibernate。它简化了数据库访问层的开发,提供了对关系型数据库的便捷操作。
    • spring-boot-starter-security:用于集成 Spring Security,提供身份验证和授权功能,帮助开发者快速实现安全机制。
    • spring-boot-starter-test:提供测试所需的依赖,包含了 JUnit、Mockito、Spring Test 等库,帮助开发者编写单元测试、集成测试和 Mock 测试。
    • spring-boot-starter-actuator:可以监控应用程序的运行状态,还可以收集应用程序的各种指标信息。
    • spring-boot-starter-aop:提供对面向切面编程(AOP)的支持,包含 Spring AOP 和 AspectJ。
    • spring-boot-starter-validation:集成了 Hibernate Validator,用于实现 Java Bean 的校验机制,通常与 Spring MVC 或 Spring Data 一起使用。

如何在springBoot应用程序中使用Jetty而不是Tomcat

  • spring-boot-starter-web 使用Tomcat作为默认的嵌入式servlet 容器,如果想要使用需要修改pom.xml
<!--从Web启动器依赖中排除Tomcat-->
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-web</artifactId>
  <exclusions>
    <exclusion>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-tomcat</artifactId>
    </exclusion>
  </exclusions>
</dependency>
<!--添加Jetty依赖-->
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-jetty</artifactId>
</dependency>

SpringBoot 的SpringBootApplication注解

  • 可以把@SpringBootApplication看作为 @Conofiguration @EnableAutoConfiguration @ComponentScan的 集合 。

    • @EnableAutoConfiguration:启动SpringBoot自动配置机制
    • @ComponentScan: 扫描被(@Controller @Service @Component)注解的 bean,注解默认会扫描该类 所在包下的所有类。
    • @Configurtion: 允许上下文中注册额外的bean或 导入 其他 配置类。