相关推荐recommended
6、Spring高频面试题
作者:mmseoamin日期:2023-12-05

1、谈谈你对SpringIOC的理解

​ IOC,也叫控制反转,是Spring用来解耦的一种设计思想,它的做法就是将对象的控制权由程序员手中反转到Spring手中。具体来说呢就是,在没有IOC之前,对象都是程序员在类中主动去创建,需要哪个创建哪个;有了IOC之后,对象会交给Spring容器创建和管理,如果哪个对象中需要其它对象属性,Spring也会自动完成依赖注入。

​ 总之一句话,IOC可以将对象的创建和对象之间依赖关系的维护交给Spring自动完成。

2、Spring中有哪些依赖注入方式

依赖注入指的是Spring给对象中属性进行赋值的过程,主要包括两种方式:

  1. 构造器依赖注入:构造器注入是指容器调用一个类的构造器创建对象时,直接传入给属性赋值

  2. Setter方法注入:Setter方法注入是指容器在创建对象完成后,通过调用属性的Setter 方法,可以属性赋值

3、你用过哪些Spring注解

我们常用的Spring注解主要分类下面几大类:

1、创建对象:@Component、@Controller、@Service、@Repository

​ 它们都可以标注在自己开发的类上,Spring会使用注解标注的类创建出对象,然后放入容器

2、依赖注入:@Autowired

​ 标注在属性或者属性对应的set方法上,Spring会根据被标注属性的类型自动对属性进行赋值

3、依赖注入:@Qualifier

​ 和@Autowired一块使用,在同一类型的bean有多个的情况下Spring会根据name进行选择注入

4、配置类:@Configuration、@Bean

​ 主要标注在配置类中,用于声明配置类和向Spring容器中放入一些配置有关的对象

5、当然还有一些平时用的不是特别多的

​ 比如:声明注解扫描的@ComponentScan,声明Bean的作用域的@Scope,用于切面编程的@Around,@Pointcut等等

4、SpringBean的作用域有几种

在Spring中作用域是用来对象的存活范围的,它支持5种作用域

  • 第一种是单例,配置为单例的对象会跟随Spring容器创建而创建,跟随Spring容器销毁而销毁,在Spring容器中无论获取多少次单例对象,得到的都是同一个,这也是Spring中的对象的默认作用域

  • 第二种是多例,配置为多例的对象在每次获取的时候才会创建,而且每次获取到的都不一样

  • 还有三种分别是request、session和application,目前已经基本不再使用

    ​ 其实,在我们平时的开发过程中,对象基本上都是配为单例的,这样可以有效的节省资源,只有单例对象存在线程安全问题时,才考虑调整为多例。

    5、Spring中的bean线程安全吗

    Spring中的Bean主要分为单例和多例

    1. 多例对象每次获取都会创建新实例,也就是说线程之间不存在Bean共享问题,也就不存在线程安全问题
    2. 单例对象是所有线程共享一个实例,因此就可能会存在线程安全问题。但是单例对象又分为无状态和有状态。
      • 无状态Bean是指只对对象的成员变量进行查询操作,不会修改成员变量的值,因此不存在线程安全问题
      • 有状态Bean需要对Bean中的成员变量进行数据更新操作,因此就可能存在线程安全问题

    所以,最终我们得出结论,在Spring中,只有有状态的单例Bean才会存在线程安全问题

    处理有状态单例Bean的线程安全问题有以下两种方法:

    1. 将Bean的作用域由单例改为多例
    2. 将需要的可变成员变量保存在ThreadLocal中, ThreadLocal本身就具备线程隔离的特性,这就相当于为每个线程提供了一个独立的变量副本,每个线程只需要操作自己的线程副本变量,从而解决线程安全问题。

    6、谈谈你对SpringAOP的理解

    ​ AOP,又叫面向切面编程,核心思想是将那些与业务无关,却为业务模块所共同调用的逻辑(例如事务处理、日志管理)封装起来,然后再动态插入到业务中的功能

    ​ 使用AOP可以减少系统的重复代码,降低模块间的耦合度,并有利于扩展和维护,Spring AOP是基于动态代理的,它底层同时支持JDK和CGLIB的代理方式,并且会根据被代理类是否有接口自动选择最合适的代理方式

    ​ 我们在开发中用到AOP的主要使用场景有:事务管理、日志、性能监视、安全检查

    7、AOP的代理有几种方式

    AOP思想的实现一般都是基于代理模式,在Java中一般采用JDK动态代理模式和CGLIB动态代理模式

    1. JDK动态代理模式只能对有接口的类进行代理,而且效率较高
    2. CGLIB可以对任意的类进行动态代理,但是效率上不如JDK

    因此在进行代理时,如果被代理类有接口,就用JDK;如果没有接口,就用CGLIB

    使用Spring的AOP,底层会自动按照这个规则进行选择,开发者也无需关心

    8、Spring的通知类型有哪些

    通知是个在方法执行前或执行后要做的动作,实际上是程序执行时要通过SpringAOP框架触发的代码段。

    Spring切面可以应用五种类型的通知:

    1. 前置通知:在某切点之前执行的通知
    2. 返回后通知:在某切点正常完成后执行的通知
    3. 抛出异常后通知:在某切点抛出异常退出时执行的通知
    4. 后置通知:在某切点退出的时候执行的通知(不论是正常返回还是异常退出)
    5. 环绕通知:包围一个切点的通知

    9、了解Spring的事务管理吗

    嗯,了解的,Spring支持编程式事务和声明式事务

    • 第一种是编程式事务指的是在代码中使用try-catch捕获异常,然后配合事务的api来手动处理事务问题

      这种方式的缺点是代码耦合,复用性低,优点是可以精确控制要增强的代码(不仅仅限于方法粒度)

    • 第二种是声明式事务,声明式事务是AOP思想的一种应用,它的核心思想是将业务方法作为切点,

      将事务处理方法作为增强,通过动态代理实现事务的管理,它的优点是降低代码耦合,提供复用

      目前在企业中基本上都是采用声明式事务的。

      10、Spring事务传播行为有几种

      事务传播行为是为了解决业务层方法之间互相调用的事务问题。

      当事务方法被另一事务方法调用时,必须指定事务应该如何传播。

      例如:方法可能继续在现有事务中运行,也可能开启一个新事务,并在自己的事务中运行。

      Spring支持7个种事务传播行为的:

      1. 必须事务:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务

      2. 必须新事务:创建一个新的事务,如果当前存在事务,则把当前事务挂起

      3. 强制事务:如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常

      4. 支持事务:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行

      5. 不支持事务:以非事务方式运行,如果当前存在事务,则把当前事务挂起

      6. 强制无事务:以非事务方式运行,如果当前存在事务,则抛出异常

      7. 嵌套事务:如果当前存在事务,则创建一个当前事务的嵌套事务来运行;如果当前没有事务,则创建一个事务

        嵌套事务是已存在事务的一个子事务,嵌套事务开始执行时,将取得一个保存点,

        如果这个嵌套事务失败,将回滚到此保存点

        嵌套事务是外部事务的一部分,只有外部事务结束后它才会被提交

      11、Spring中的事务是如何实现的

      ​ Spring事务底层是基于数据库事务和AOP机制的

      ​ 首先Spring会为使用了@Transactional注解的Bean代理对象,当调用代理对象的方法时,会先判断该方法上是否加了@Transactional注解。如果加了,那么则利用事务管理器创建一个数据库连接,并且禁止此连接的自动提交事务

      ​ 然后执行当前方法,方法中会执行sql ,执行完当前方法后,如果没有出现异常就直接提交事务;如果出现了异常,并且这个异常是需要回滚的就会回滚事务,否则仍然提交事务。

      ​ Spring事务的隔离级别对应的就是数据库的隔离级别 ,Spring事务的传播机制是基于数据库连接来做的,一个数据库连接一个事务,如果传播机制配置为 需要新开一个事务,那么实际上就是重新建立一个数据库连接,在此新数据库连接上执⾏sql

      12、Spring中的设计模式有哪些

      • 工厂模式:Spring使用工厂模式通过 BeanFactory和 ApplicationContext创建 bean 对象
      • 单例模式: Spring 中的 bean 默认都是单例的
      • 代理模式:Spring 的 AOP 功能用到了 JDK 的动态代理和 CGLIB 字节码生成技术
      • 模板方法:用来解决代码重复的问题。比如 RestTemplate、jdbcTemplate、 JpaTemplate 等以 Template 结尾的对数据库操作的类,它们就使用到了模板模式
      • 观察者模式: Spring 事件驱动模型就是观察者模式很经典的一个应用。定义对象键一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知被制动更新,如 Spring 中 listener 的实现 ApplicationListener。

        13、Spring是怎么解决循环依赖的(待整理)

        整个IOC容器解决循环依赖,用到的几个重要成员:

        singletonObjects:一级缓存,存放完全初始化好的 Bean 的集合,从这个集合中取出来的 Bean 可以立马返回 earlySingletonObjects:二级缓存,存放创建好但没有初始化属性的 Bean 的集合,它用来解决循环依赖 singletonFactories:三级缓存,存放单实例 Bean 工厂的集合 singletonsCurrentlyInCreation:存放正在被创建的 Bean 的集合

        IOC 容器解决循环依赖的思路:

        1. 初始化 Bean 之前,将这个 BeanName 放入三级缓存

        2. 创建 Bean 将准备创建的 Bean 放入 singletonsCurrentlyInCreation (正在创建的 Bean)

        3. createNewInstance 方法执行完后执行 addSingletonFactory,将这个实例化但没有属性赋值的 Bean 放入二级缓存,并从三级缓存中移除

        4. 属性赋值&自动注入时,引发关联创建

        5. 关联创建时,检查“正在被创建的 Bean”中是否有即将注入的 Bean。如果有,检查二级缓存中是否有当前创建好但没有赋值初始化的 Bean。如果没有,检查三级缓存中是否有!正在创建中的 Bean。至此一般会有,将这个 Bean 放入二级缓存,并从三级缓存中移除

        6. 之后 Bean 被成功注入,最后执行 addSingleton,将这个完全创建好的 Bean 放入一级缓存,从二级缓存和三级缓存移除,并记录已经创建了的单实例 Bean

        14、SpringBean的生命周期

        6、Spring高频面试题,在这里插入图片描述,第1张

        1. Bean 容器找到配置文件中 Spring Bean 的定义。
        2. Bean 容器利用 Java Reflection API 创建一个 Bean 的实例。
        3. 如果涉及到一些属性值,利用 set()方法设置一些属性值。
        4. 如果 Bean 实现了 BeanNameAware 接口,调用 setBeanName()方法,传入 Bean 的名字。
        5. 如果 Bean 实现了 BeanClassLoaderAware 接口,调用 setBeanClassLoader()方法,传入 ClassLoader 对象的实例。
        6. 如果 Bean 实现了 BeanFactoryAware 接口,调用 setBeanClassFacotory()方法,传入 ClassLoader 对象的实例。
        7. 与上面的类似,如果实现了其他*Aware 接口,就调用相应的方法。
        8. 如果有和加载这个 Bean 的 Spring 容器相关的 BeanPostProcessor 对象,执行 postProcessBeforeInitialization()方法。
        9. 如果 Bean 实现了 InitializingBean 接口,执行 afeterPropertiesSet()方法。
        10. 如果 Bean 在配置文件中的定义包含 init-method 属性,执行指定的方法。
        11. 如果有和加载这个 Bean 的 Spring 容器相关的 BeanPostProcess 对象,执行 postProcessAfterInitialization()方法。
        12. 当要销毁 Bean 的时候,如果 Bean 实现了 DisposableBean 接口,执行 destroy()方法。
        13. 当要销毁 Bean 的时候,如果 Bean 在配置文件中的定义包含 destroy-method 属性,执行指定的方法。

        总结以上步骤,核心主干主要就是五部分构成:

        1. 构造 Bean 对象
        2. 设置 Bean 属性
        3. 初始化回调
        4. Bean 调用
        5. 销毁 Bean

        15、SpringMVC执行流程

        MVC 是 Model — View — Controler 的简称,它是一种架构模式,它分离了表现与交互。它被分为三个核心部件:模型、视图、控制器。

        6、Spring高频面试题,在这里插入图片描述,第2张

        具体流程如下所示:

        1、用户发送出请求到前端控制器DispatcherServlet。

        2、DispatcherServlet收到请求调用HandlerMapping(处理器映射器)。

        3、HandlerMapping找到具体的处理器(可查找xml配置或注解配置),生成处理器对象及处理器拦截器(如果有),再一起返回给DispatcherServlet。

        4、DispatcherServlet调用HandlerAdapter(处理器适配器)。

        5、HandlerAdapter经过适配调用具体的处理器(Handler/Controller)。

        6、Controller执行完成返回ModelAndView对象。

        7、HandlerAdapter将Controller执行结果ModelAndView返回给DispatcherServlet。

        8、DispatcherServlet将ModelAndView传给ViewReslover(视图解析器)。

        9、ViewReslover解析后返回具体View(视图)。

        10、DispatcherServlet根据View进行渲染视图(即将模型数据填充至视图中)。

        11、DispatcherServlet响应用户。

        16、SpringMVC的常用注解有哪些

        我们常用的Springmvc注解主要分类下面几大类:

        1、用于声明Bean到Springmvc容器:@Controller、@RestController

        ​ 区别在于后者还可以将返回的集合或对象转换为JSON直接返回

        2、设置请求路径:@RequestMapping、@GetMapping、@PostMapping 、@PutMapping、@DeleteMapping

        ​ 第一个是通用的,可以接收各种类型的请求;后面四个只能直接对应类型的请求

        3、接收请求参数:

        ​ @RequestBody: 接收请求体中的json数据

        ​ @PathViriable:接收请求路径中的参数

        ​ @RequestHeader:接收请求头中的参数

        ​ @RequestParam:一般用于给参数设置默认值或者完成请求参数和controller方法参数的映射

        17、SpringMVC如何处理统一异常

        SpringMVC的异常处理底层是通过AOP实现的,它的核心思想是将异常处理的代码和业务逻辑代码分离开来

        使用它之后,我们在自己的业务代码中不需要在处理异常,有异常直接就上抛到框架中

        框架就会将异常交给自定义的全局异常处理器中统一处理

        自定义全局异常处理器,会用到两个注解:

        1. @RestControllerAdvice 标注在类上,声明被标注的类是一个用于专门处理异常的类
        2. @ExceptionHandler 标注在异常处理类中的方法上,声明被标注的方法可以处理哪些异常