【SSM面试篇】Spring、SpringMVC、SpringBoot、Mybatis高频八股汇总

目录

Spring

[1. 说一下你对 Spring 的理解](#1. 说一下你对 Spring 的理解)

[2. Spring的核心思想说说你的理解?](#2. Spring的核心思想说说你的理解?)

[3. Spring IOC和AOP 的区别?](#3. Spring IOC和AOP 的区别?)

[4. Spring的AOP介绍一下](#4. Spring的AOP介绍一下)

[5. IOC和AOP是通过什么机制来实现的?](#5. IOC和AOP是通过什么机制来实现的?)

[6. 怎么理解SpringIOC?](#6. 怎么理解SpringIOC?)

[7. 依赖倒置,依赖注入,控制反转分别是什么?](#7. 依赖倒置,依赖注入,控制反转分别是什么?)

[8. 依赖注入了解吗?怎么实现依赖注入的?](#8. 依赖注入了解吗?怎么实现依赖注入的?)

[9. SpringAOP主要想解决什么问题?](#9. SpringAOP主要想解决什么问题?)

[10. SpringAOP的原理了解吗?](#10. SpringAOP的原理了解吗?)

[11. 动态代理是什么?](#11. 动态代理是什么?)

[12. AOP实现有哪些注解?](#12. AOP实现有哪些注解?)

[13. Spring是如何解决循环依赖的?](#13. Spring是如何解决循环依赖的?)

[14. Spring三级缓存的数据结构是什么?](#14. Spring三级缓存的数据结构是什么?)

[15. Spring框架中都用到了哪些设计模式?](#15. Spring框架中都用到了哪些设计模式?)

[16. Spring 常用注解有什么?](#16. Spring 常用注解有什么?)

[17. Spring的事务什么情况下会失效?](#17. Spring的事务什么情况下会失效?)

[18. Spring的事务,使用this调用是否生效?](#18. Spring的事务,使用this调用是否生效?)

[19. Bean的生命周期说一下?](#19. Bean的生命周期说一下?)

[20. Bean是否单例?](#20. Bean是否单例?)

[21. Bean的单例和非单例,生命周期是否一样?](#21. Bean的单例和非单例,生命周期是否一样?)

[22. Spring bean的作用域有哪些?](#22. Spring bean的作用域有哪些?)

[23. Spring容器里存的是什么?](#23. Spring容器里存的是什么?)

[24. Bean注入和xml注入最终得到了相同的效果,它们在底层是怎样做的?](#24. Bean注入和xml注入最终得到了相同的效果,它们在底层是怎样做的?)

SpringMVC

[25. MVC分层介绍一下](#25. MVC分层介绍一下)

[26. SpringMVC的处理流程?](#26. SpringMVC的处理流程?)

[27. Handlermapping 和 Handleradapter有了解吗?](#27. Handlermapping 和 Handleradapter有了解吗?)

SpringBoot

[28. 为什么使用SpringBoot,SpringBoot优点?](#28. 为什么使用SpringBoot,SpringBoot优点?)

[29. SpringBoot比Spring好在哪里?](#29. SpringBoot比Spring好在哪里?)

[30. SpringBoot用到哪些设计模式?](#30. SpringBoot用到哪些设计模式?)

[31. 怎么理解SpringBoot中的约定大于配置?](#31. 怎么理解SpringBoot中的约定大于配置?)

[32. SpringBoot的项目结构是怎么样的?](#32. SpringBoot的项目结构是怎么样的?)

[33. SpringBoot自动装配原理是什么?](#33. SpringBoot自动装配原理是什么?)

[34. 说几个启动器(starter)?](#34. 说几个启动器(starter)?)

[35. SpringBoot里面有哪些重要的注解?还有一个配置相关的注解是哪个?](#35. SpringBoot里面有哪些重要的注解?还有一个配置相关的注解是哪个?)

[36. SpringBoot怎么开启事务?](#36. SpringBoot怎么开启事务?)

[37. Springboot怎么做到导入就可以直接使用的?](#37. Springboot怎么做到导入就可以直接使用的?)

[38. SpringBoot 过滤器和拦截器说一下?](#38. SpringBoot 过滤器和拦截器说一下?)

Mybatis

[39. 与传统的JDBC相比,MyBatis的优点?](#39. 与传统的JDBC相比,MyBatis的优点?)

[40. MyBatis觉得在哪方面做的比较好?](#40. MyBatis觉得在哪方面做的比较好?)

[41. 还记得JDBC连接数据库的步骤吗?](#41. 还记得JDBC连接数据库的步骤吗?)

[42. 如果项目中要用到原生的MyBatis去查询,该怎样写?](#42. 如果项目中要用到原生的MyBatis去查询,该怎样写?)

[43. Mybatis里的 # 和 的区别?](# 和 的区别?)

[44. MybatisPlus和Mybatis的区别?](#44. MybatisPlus和Mybatis的区别?)

[45. MyBatis运用了哪些常见的设计模式?](#45. MyBatis运用了哪些常见的设计模式?)


Spring

1. 说一下你对 Spring 的理解

Spring 框架核心特性包括:

  • IoC 容器:Spring 通过控制反转实现了对象的创建和对象间的依赖关系管理。开发者只需要定义好 Bean 及其依赖关系,Spring 容器负责创建和组装这些对象。
  • AOP:面向切面编程,允许开发者定义横切关注点,例如事务管理、安全控制等,独立于业务逻辑的代码。通过 AOP,可以将这些关注点模块化,提高代码的可维护性和可重用性。
  • 事务管理:Spring 提供了一致的事务管理接口,支持声明式和编程式事务。开发者可以轻松地进行事务管理,而无需关心具体的事务 API。
  • MVC 框架:Spring MVC 是一个基于 Servlet API 构建的 Web 框架,采用了模型 - 视图 - 控制器(MVC)架构。它支持灵活的 URL 到页面控制器的映射,以及多种视图技术。

2. Spring的核心思想说说你的理解?

核心思想 解决的问题 实现手段 典型应用场景
IOC 对象创建与依赖管理的高耦合 容器管理 Bean 生命周期 动态替换数据库实现、服务组装
DI 依赖关系的硬编码问题 Setter / 构造器 / 注解注入 注入数据源、服务层依赖 DAO 层
AOP 横切逻辑分散在业务代码中 动态代理与切面配置 日志、事务、权限校验统一处理

Spring 通过这IOCDIAOP三大核心思想,实现了轻量级、高内聚低耦合的企业级应用开发框架,成为 Java 生态中不可或缺的基石。

3. Spring IOC和AOP 的区别?

Spring IoC 和 AOP 区别:

  • IoC:即控制反转的意思,它是一种创建和获取对象的技术思想,依赖注入 (DI) 是实现这种技术的一种方式。传统开发过程中,我们需要通过 new 关键字来创建对象。使用 IoC 思想开发方式的话,我们不通过 new 关键字创建对象,而是通过 IoC 容器来帮我们实例化对象。 通过 IoC 的方式,可以大大降低对象之间的耦合度。
  • AOP:是面向切面编程,能够将那些与业务无关,却为业务模块所共同调用的逻辑封装起来,以减少系统的重复代码,降低模块间的耦合度。Spring AOP 就是基于动态代理的,如果要代理的对象,实现了某个接口,那么 Spring AOP 会使用 JDK Proxy,去创建代理对象,而对于没有实现接口的对象,就无法使用 JDK Proxy 去进行代理了,这时候 Spring AOP 会使用 Cglib 生成一个被代理对象的子类来作为代理。

在 Spring 框架中,IOC 和 AOP 结合使用,可以更好地实现代码的模块化和分层管理。例如:

  • 通过 IOC 容器管理对象的依赖关系,然后通过 AOP 将横切关注点统一切入到需要的业务逻辑中。
  • 使用 IOC 容器管理 Service 层和 DAO 层的依赖关系,然后通过 AOP 在 Service 层实现事务管理、日志记录等横切功能,使得业务逻辑更加清晰和可维护。

4. Spring的AOP介绍一下

Spring AOP 是 Spring 框架中的一个重要模块,用于实现面向切面编程。

我们知道,Java 就是一门面向对象编程的语言,在 OOP 中最小的单元就是 "Class 对象",但是在 AOP 中最小的单元是 "切面 "。一个 "切面" 可以包含很多种类型和对象,对它们进行模块化管理,例如事务管理。

在面向切面编程的思想里面,把功能分为两种

  • 核心业务:登陆、注册、增、删、改、查、都叫核心业务
  • 周边功能:日志、事务管理这些次要的为周边业务

在面向切面编程中,核心业务功能和周边功能是分别独立进行开发,两者不是耦合的,然后把切面 功能和核心业务功能 "编织 " 在一起,这就叫AOP

AOP 能够将那些与业务无关,却为业务模块所共同调用的逻辑或责任(例如事务处理、日志管理、权限控制等)封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可拓展性和可维护性。

在 AOP 中有以下几个概念:

  • AspectJ:切面,只是一个概念,没有具体的接口或类与之对应,是 Join point,Advice 和 Pointcut 的一个统称。
  • Join point:连接点,指程序执行过程中的一个点,例如方法调用、异常处理等。在 Spring AOP 中,仅支持方法级别的连接点。
  • Advice :通知,即我们定义的一个切面中的横切逻辑,有 "around ","before " 和 "after" 三种类型。在很多的 AOP 实现框架中,Advice 通常作为一个拦截器,也可以包含许多个拦截器作为一条链路围绕着 Join point 进行处理。
  • Pointcut:切点,用于匹配连接点,一个 AspectJ 中包含哪些 Join point 需要由 Pointcut 进行筛选。
  • Introduction:引介,让一个切面可以声明被通知的对象实现任何他们没有真正实现的额外的接口。例如可以让一个代理对象代理两个目标类。
  • Weaving:织入,在有了连接点、切点、通知以及切面,如何将它们应用到程序中呢?没错,就是织入,在切点的引导下,将通知逻辑插入到目标方法上,使得我们的通知逻辑在方法调用时得以执行。
  • AOP proxy:AOP 代理,指在 AOP 实现框架中实现切面协议的对象。在 Spring AOP 中有两种代理,分别是 JDK 动态代理和 CGLIB 动态代理。
  • Target object:目标对象,就是被代理的对象。

Spring AOP 是基于 JDK 动态代理和 Cglib 提升实现的,两种代理方式都属于运行时的一个方式,所以它没有编译时的一个处理,那么因此 Spring 是通过 Java 代码实现的。

5. IOC和AOP是通过什么机制来实现的?

Spring IOC 实现机制

  • 反射:Spring IOC 容器利用 Java 的反射机制动态地加载类、创建对象实例及调用对象方法,反射允许在运行时检查类、方法、属性等信息,从而实现灵活的对象实例化和管理。
  • 依赖注入:IOC 的核心概念是依赖注入,即容器负责管理应用程序组件之间的依赖关系。Spring 通过构造函数注入、属性注入或方法注入,将组件之间的依赖关系描述在配置文件中或使用注解。
  • 设计模式 - 工厂模式:Spring IOC 容器通常采用工厂模式来管理对象的创建和生命周期。容器作为工厂负责实例化 Bean 并管理它们的生命周期,将 Bean 的实例化过程交给容器来管理。
  • 容器实现:Spring IOC 容器是实现 IOC 的核心,通常使用 BeanFactory 或 ApplicationContext 来管理 Bean。BeanFactory 是 IOC 容器的基本形式,提供基本的 IOC 功能;ApplicationContext 是 BeanFactory 的扩展,并提供更多企业级功能。

Spring AOP 实现机制

Spring AOP 的实现依赖于动态代理技术。动态代理是在运行时动态生成代理对象,而不是在编译时。它允许开发者在运行时指定要代理的接口和行为,从而实现在不修改源码的情况下增强方法的功能。

Spring AOP 支持两种动态代理:

  • 基于 JDK 的动态代理:使用 java.lang.reflect.Proxy 类和 java.lang.reflect.InvocationHandler 接口实现。这种方式需要代理的类实现一个或多个接口。
  • 基于 CGLIB 的动态代理:当被代理的类没有实现接口时,Spring 会使用 CGLIB 库生成一个被代理类的子类作为代理。CGLIB(Code Generation Library)是一个第三方代码生成库,通过继承方式实现代理。

6. 怎么理解SpringIOC?

IOC:Inversion Of Control,即控制反转,是一种设计思想。在传统的 Java SE 程序设计中,我们直接在对象内部通过 new 的方式来创建对象,是程序主动创建依赖对象;

而在Spring程序设计中,IOC 是有专门的容器去控制对象。

所谓控制就是对象的创建、初始化、销毁。

  • 创建对象:原来是 new 一个,现在是由 Spring 容器创建。
  • 初始化对象:原来是对象自己通过构造器或者 setter 方法给依赖的对象赋值,现在是由 Spring 容器自动注入。
  • 销毁对象:原来是直接给对象赋值 null 或做一些销毁操作,现在是 Spring 容器管理生命周期负责销毁对象。

总结:IOC 解决了繁琐的对象生命周期的操作,解耦了我们的代码。所谓反转:其实是反转的控制权,前面提到是由 Spring 来控制对象的生命周期,那么对象的控制就完全脱离了我们的控制,控制权交给了 Spring 。这个反转是指:我们由对象的控制者变成了 IOC 的被动控制者。

7. 依赖倒置,依赖注入,控制反转分别是什么?

  • 控制反转 :"控制" 指的是对程序执行流程的控制,而 "反转" 指的是在没有使用框架之前,程序员自己控制整个程序的执行。在使用框架之后,整个程序的执行流程通过框架来控制。流程的控制权从程序员 "反转" 给了框架。
  • 依赖注入 :依赖注入和控制反转恰恰相反,它是一种具体的编码技巧。我们不通过 new 的方式在类内部创建依赖类的对象,而是将依赖的类对象在外部创建好之后,通过构造函数、函数参数等方式传递(或注入)给类来使用。
  • 依赖倒置:这条原则跟控制反转有点类似,主要用来指导框架层面的设计。高层模块不依赖低层模块,它们共同依赖同一个抽象。抽象不要依赖具体实现细节,具体实现细节依赖抽象。

8. 依赖注入了解吗?怎么实现依赖注入的?

在传统编程中,当一个类需要使用另一个类的对象时,通常会在该类内部通过 new 关键字来创建依赖对象,这使得类与类之间的耦合度较高。

而依赖注入则是将对象的创建和依赖关系的管理交给 Spring 容器来完成,类只需要声明自己所依赖的对象,容器会在运行时将这些依赖对象注入到类中,从而降低了类与类之间的耦合度,提高了代码的可维护性和可测试性。

具体到 Spring 中,常见的依赖注入的实现方式,比如构造器注入、Setter 方法注入,还有字段注入。

  • 构造器注入通过构造函数传递依赖对象,保证对象初始化时依赖已就绪
java 复制代码
@Service
public class UserService {
    private final UserRepository userRepository;

    // 构造器注入(Spring 4.3+ 自动识别单构造器,无需显式@Autowired)
    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }
}
  • Setter 方法注入通过 Setter 方法设置依赖,灵活性高,但依赖可能未完全初始化
java 复制代码
public class PaymentService {
    private PaymentGateway gateway;

    @Autowired
    public void setGateway(PaymentGateway gateway) {
        this.gateway = gateway;
    }
}
  • 字段注入直接通过 @Autowired 注解字段,代码简洁但隐藏依赖关系,不推荐生产代码
java 复制代码
@Service
public class OrderService {
    @Autowired
    private OrderRepository orderRepository;
}

9. SpringAOP主要想解决什么问题?

它的目的是对于面向对象思维的一种补充,而不是像引入命令式、函数式编程思维让他顺应另一种开发场景。在我个人的理解下AOP 更像是一种对于不支持多继承的弥补,除开对象的主要特征(我更喜欢叫 "强共性")被抽象为了一条继承链路,对于一些 "弱共性",AOP可以统一对他们进行抽象和集中处理。

举一个简单的例子,打印日志。需要打印日志可能是许多对象的一个共性,这在企业级开发中十分常见,但是日志的打印并不反应这个对象的主要共性。而日志的打印又是一个具体的内容,它并不抽象,所以它的工作也不可以用接口来完成。而如果利用继承,打印日志的工作又横跨继承树下面的多个同级子节点,强行侵入到继承树内进行归纳会干扰这些强共性的区分。

这时候,我们就需要AOP 了。AOP 首先在一个Aspect (切面)里定义了一些Advice (增强),其中包含具体实现的代码,同时整理了切入点,切入点的粒度是方法。最后,我们将这些Advice织入到对象的方法上,形成了最后执行方法时面对的完整方法。

10. SpringAOP的原理了解吗?

Spring AOP 的实现依赖于动态代理技术。动态代理是在运行时动态生成代理对象,而不是在编译时。它允许开发者在运行时指定要代理的接口和行为,从而实现在不修改源码的情况下增强方法的功能。

Spring AOP 支持两种动态代理:

  • 基于 JDK 的动态代理:使用 java.lang.reflect.Proxy 类和 java.lang.reflect.InvocationHandler 接口实现。这种方式需要代理的类实现一个或多个接口。
  • 基于 CGLIB 的动态代理:当被代理的类没有实现接口时,Spring 会使用 CGLIB 库生成一个被代理类的子类作为代理。CGLIB(Code Generation Library)是一个第三方代码生成库,通过继承方式实现代理。

11. 动态代理是什么?

Java 的动态代理是一种在运行时动态创建代理对象的机制,主要用于在不修改原始类的情况下对方法调用进行拦截和增强。

Java 动态代理主要分为两种类型:

  • 基于接口的代理(JDK 动态代理) : 这种类型的代理要求目标对象必须实现至少一个接口。Java 动态代理会创建一个实现了相同接口的代理类,然后在运行时动态生成该类的实例。这种代理的实现核心是 java.lang.reflect.Proxy 类和 java.lang.reflect.InvocationHandler 接口。每一个动态代理类都必须实现 InvocationHandler 接口,并且每个代理类的实例都关联到一个 handler 。当通过代理对象调用一个方法时,这个方法的调用会被转发为由 InvocationHandler 接口的 invoke() 方法来进行调用。
  • 基于类的代理(CGLIB 动态代理): CGLIB(Code Generation Library)是一个强大的高性能的代码生成库,它可以在运行时动态生成一个目标类的子类。CGLIB 代理不需要目标类实现接口,而是通过继承的方式创建代理类。因此,如果目标对象没有实现任何接口,可以使用 CGLIB 来创建动态代理。

12. AOP实现有哪些注解?

常用的注解包括:

  • @Aspect:用于定义切面,标注在切面类上。
  • @Pointcut:定义切点,标注在方法上,用于指定连接点。
  • @Before:在方法执行之前执行通知。
  • @After:在方法执行之后执行通知。
  • @Around:在方法执行前后都执行通知。
  • @AfterReturning:在方法执行后返回结果后执行通知。
  • @AfterThrowing:在方法抛出异常后执行通知。
  • @Advice:通用的通知类型,可以替代 @Before、@After 等。

13. Spring是如何解决循环依赖的?

循环依赖指的是两个类中的属性相互依赖对方:例如A类中有 B属性,B 类中有 A属性,从而形成了一个依赖闭环,如下图。

循环依赖问题在 Spring 中主要有三种情况:

  • 第一种:通过构造方法进行依赖注入时产生的循环依赖问题。
  • 第二种:通过 setter 方法进行依赖注入且是在多例(原型)模式下产生的循环依赖问题。
  • 第三种:通过 setter 方法进行依赖注入且是在单例模式下产生的循环依赖问题。

只有【第三种方式】的循环依赖问题被 Spring 解决了,其他两种方式在遇到循环依赖问题时,Spring 都会产生异常。

Spring 解决单例模式下的 setter 循环依赖问题的主要方式是通过三级缓存解决循环依赖。三级缓存指的是 Spring 在创建 Bean 的过程中,通过三级缓存来缓存正在创建的 Bean,以及已经创建完成的 Bean 实例。具体步骤如下:

  • 实例化 Bean:Spring 在实例化 Bean 时,会先创建一个空的 Bean 对象,并将其放入一级缓存中。
  • 属性赋值:Spring 开始对 Bean 进行属性赋值,如果发现循环依赖,会将当前 Bean 对象提前暴露给后续需要依赖的 Bean(通过提前暴露的方式解决循环依赖)。
  • 初始化 Bean:完成属性赋值后,Spring 将 Bean 进行初始化,并将其放入二级缓存中。
  • 注入依赖:Spring 继续对 Bean 进行依赖注入,如果发现循环依赖,会从二级缓存中获取已经完成初始化的 Bean 实例。

通过三级缓存的机制,Spring 能够在处理循环依赖时,确保及时暴露正在创建的 Bean 对象,并能够正确地注入已经初始化的 Bean 实例,从而解决循环依赖问题,保证应用程序的正常运行。

14. Spring三级缓存的数据结构是什么?

都是 Map 类型的缓存,比如 Map {k:name; v:bean}。

  • 一级缓存(Singleton Objects) :这是一个 Map 类型的缓存,存储的是已经完全初始化好的 bean,即完全准备好可以使用的 bean 实例。键是 bean 的名称,值是 bean 的实例。这个缓存在 DefaultSingletonBeanRegistry 类中的 singletonObjects 属性中。
  • 二级缓存(Early Singleton Objects) :这同样是一个 Map 类型的缓存,存储的是早期的 bean 引用,即已经实例化但还未完全初始化的 bean。这些 bean 已经被实例化,但是可能还没有进行属性注入等操作。这个缓存在 DefaultSingletonBeanRegistry 类中的 earlySingletonObjects 属性中。
  • 三级缓存(Singleton Factories) :这也是一个 Map 类型的缓存,存储的是 ObjectFactory 对象,这些对象可以生成早期的 bean 引用。当一个 bean 正在创建过程中,如果它被其他 bean 依赖,那么这个正在创建的 bean 就会通过这个 ObjectFactory 来创建一个早期引用,从而解决循环依赖的问题。这个缓存在 DefaultSingletonBeanRegistry 类中的 singletonFactories 属性中。

15. Spring框架中都用到了哪些设计模式?

  • 工厂设计模式:Spring 使用工厂模式通过 BeanFactory、ApplicationContext 创建 bean 对象。
  • 代理设计模式:Spring AOP 功能的实现。
  • 单例设计模式:Spring 中的 Bean 默认都是单例的。
  • 模板方法模式:Spring 中 jdbcTemplate、hibernateTemplate 等以 Template 结尾的对数据库操作的类,它们就使用到了模板模式。
  • 包装器设计模式:我们的项目需要连接多个数据库,而且不同的客户在每次访问中根据需要会去访问不同的数据库。这种模式让我们可以根据客户的需求能够动态切换不同的数据源。
  • 观察者模式:Spring 事件驱动模型就是观察者模式很经典的一个应用。
  • 适配器模式:Spring AOP 的增强或通知 (Advice) 使用到了适配器模式、spring MVC 中也是用到了适配器模式适配 Controller。

16. Spring 常用注解有什么?

@Autowired 注解

@Autowired:主要用于自动装配 bean。当 Spring容器 中存在与要注入的属性类型匹配的 bean 时,它会自动将 bean 注入到属性中。就跟我们new对象一样。

示例代码:

java 复制代码
@Component
public class MyService {
}

@Component
public class MyController {

    @Autowired
    private MyService myService;
}

在上面的示例代码中,MyController类中的myService属性被@Autowired注解标记,Spring 会自动将MyService类型的 bean 注入到myService属性中。

@Component

这个注解用于标记一个类作为 Spring 的 bean。当一个类被@Component注解标记时,Spring 会将其实例化为一个 bean,并将其添加到 Spring 容器中。在上面讲解@Autowired的时候也看到了,示例代码:

java 复制代码
@Component
public class MyComponent {
}

在上面的示例代码中,MyComponent类被@Component注解标记,Spring 会将其实例化为一个 bean,并将其添加到 Spring 容器中。

@Configuration

@Configuration,注解用于标记一个类作为 Spring 的配置类。配置类可以包含@Bean注解的方法,用于定义和配置 bean,作为全局配置。示例代码:

java 复制代码
@Configuration
public class MyConfiguration {

    @Bean
    public MyBean myBean() {
        return new MyBean();
    }
}

@Bean

@Bean注解用于标记一个方法作为 Spring 的 bean 工厂方法。当一个方法被@Bean注解标记时,Spring 会将该方法的返回值作为一个 bean,并将其添加到 Spring 容器中,如果自定义配置,经常用到这个注解。

java 复制代码
@Configuration
public class MyConfiguration {

    @Bean
    public MyBean myBean() {
        return new MyBean();
    }
}

@Service

@Service,这个注解用于标记一个类作为服务层的组件。它是@Component注解的特例,用于标记服务层的 bean,一般标记在业务 service 的实现类。

java 复制代码
@Service
public class MyServiceImpl {
}

@Repository

@Repository注解用于标记一个类作为数据访问层的组件。它也是@Component注解的特例,用于标记数据访问层的 bean。这个注解很容易被忽略,导致数据库无法访问。

java 复制代码
@Repository
public class MyRepository {
}

在上面的示例代码中,MyRepository类被@Repository注解标记,Spring 会将其实例化为一个 bean,并将其添加到 Spring 容器中。

@Controller

@Controller注解用于标记一个类作为控制层的组件。它也是@Component注解的特例,用于标记控制层的 bean。这是 MVC 结构的另一个部分,加在控制层

java 复制代码
@Controller
public class MyController {
}

在上面的示例代码中,MyController类被@Controller注解标记,Spring 会将其实例化为一个 bean,并将其添加到 Spring 容器中。

17. Spring的事务什么情况下会失效?

Spring Boot 通过 Spring 框架的事务管理模块来支持事务操作。事务管理在 Spring Boot 中通常是通过@Transactional注解来实现的。事务可能会失效的一些常见情况包括:

  • 未捕获异常:如果一个事务方法中发生了未捕获的异常,并且异常未被处理或传播到事务边界之外,那么事务会失效,所有的数据库操作会回滚。
  • 非受检异常:默认情况下,Spring 对非受检异常(RuntimeException 或其子类)进行回滚处理,这意味着当事务方法中抛出这些异常时,事务会回滚。
  • 事务传播属性设置不当 :如果在多个事务之间存在事务嵌套,且事务传播属性配置不正确,可能导致事务失效。特别是在方法内部调用有@Transactional注解的方法时要特别注意。
  • 多数据源的事务管理 :如果在使用多数据源时,事务管理没有正确配置或者存在多个@Transactional注解时,可能会导致事务失效。
  • 跨方法调用事务问题 :如果一个事务方法内部调用另一个方法,而这个被调用的方法没有@Transactional注解,这种情况下外层事务可能会失效。
  • 事务在非公开方法中失效 :如果@Transactional注解标注在私有方法上或者非 public 方法上,事务也会失效。

18. Spring的事务,使用this调用是否生效?

不能生效。

因为 Spring 事务是通过代理对象来控制的,只有通过代理对象的方法调用才会应用事务管理的相关规则。当使用 this 直接调用时,是绕过了 Spring 的代理机制,因此不会应用事务设置。

19. Bean的生命周期说一下?

  1. Spring 启动,查找并加载需要被 Spring 管理的 bean,进行 Bean 的实例化
  2. Bean 实例化后对将 Bean 的引入和值注入到 Bean 的属性中
  3. 如果 Bean 实现了BeanNameAware接口的话,Spring 将 Bean 的 Id 传递给setBeanName()方法
  4. 如果 Bean 实现了BeanFactoryAware接口的话,Spring 将调用setBeanFactory()方法,将BeanFactory容器实例传入
  5. 如果 Bean 实现了ApplicationContextAware接口的话,Spring 将调用 Bean 的setApplicationContext()方法,将 bean 所在应用上下文引用传入进来。
  6. 如果 Bean 实现了BeanPostProcessor接口,Spring 就将调用他们的postProcessBeforeInitialization()方法。
  7. 如果 Bean 实现了InitializingBean接口,Spring 将调用他们的afterPropertiesSet()方法。类似的,如果 bean 使用init-method声明了初始化方法,该方法也会被调用
  8. 如果 Bean 实现了BeanPostProcessor接口,Spring 就将调用他们的postProcessAfterInitialization()方法。
  9. 此时,Bean 已经准备就绪,可以被应用程序使用了。他们将一直驻留在应用上下文中,直到应用上下文被销毁。
  10. 如果 bean 实现了DisposableBean接口,Spring 将调用它的destory()接口方法,同样,如果 bean 使用了destory-method声明销毁方法,该方法也会被调用。

20. Bean是否单例?

Spring 中的 Bean 默认都是单例的。

就是说,每个 Bean 的实例只会被创建一次,并且会被存储在 Spring 容器的缓存中,以便在后续的请求中重复使用。这种单例模式可以提高应用程序的性能和内存效率。

但是,Spring 也支持将 Bean 设置为多例模式,即每次请求都会创建一个新的 Bean 实例。要将 Bean 设置为多例模式,可以在 Bean 定义中通过设置scope属性为"prototype"来实现。

需要注意的是,虽然 Spring 的默认行为是将 Bean 设置为单例模式,但在一些情况下,使用多例模式是更为合适的,例如在创建状态不可变的 Bean 或有状态 Bean 时。此外,需要注意的是,如果 Bean 单例是有状态的,那么在使用时需要考虑线程安全性问题。

21. Bean的单例和非单例,生命周期是否一样?

不一样, Spring Bean 的生命周期完全由 IoC 容器控制。Spring 只帮我们管理单例模式 Bean 的完整生命周期,对于 prototype 的 Bean,Spring 在创建好交给使用者之后,则不会再管理后续的生命周期。

具体区别如下:

阶段 单例(Singleton) 非单例(如 Prototype)
创建时机 容器启动时创建(或首次请求时,取决于配置)。 每次请求时创建新实例。
初始化流程 完整执行生命周期流程(属性注入、Aware 接口、初始化方法等)。 每次创建新实例时都会完整执行生命周期流程(仅到初始化完成)。
销毁时机 容器关闭时销毁,触发 DisposableBeandestroy-method 容器不管理销毁,需由调用者自行释放资源(Spring 不跟踪实例)。
内存占用 单实例常驻内存,高效但需注意线程安全。 每次请求生成新实例,内存开销较大,需手动管理资源释放。
适用场景 无状态服务(如 Service、DAO 层)。 有状态对象(如用户会话、临时计算对象)。

22. Spring bean的作用域有哪些?

Spring 框架中的 Bean 作用域(Scope)定义了 Bean 的生命周期和可见性。不同的作用域影响着 Spring 容器如何管理这些 Bean 的实例,包括它们如何被创建、如何被销毁以及它们是否可以被多个用户共享。

Spring 支持几种不同的作用域,以满足不同的应用场景需求。以下是一些主要的 Bean 作用域:

  • Singleton(单例):在整个应用程序中只存在一个 Bean 实例。默认作用域,Spring 容器中只会创建一个 Bean 实例,并在容器的整个生命周期中共享该实例。
  • Prototype(原型):每次请求时都会创建一个新的 Bean 实例。次从容器中获取该 Bean 时都会创建一个新实例,适用于状态非常瞬时的 Bean。
  • Request(请求):每个 HTTP 请求都会创建一个新的 Bean 实例。仅在 Spring Web 应用程序中有效,每个 HTTP 请求都会创建一个新的 Bean 实例,适用于 Web 应用中需求局部性的 Bean。
  • Session(会话):Session 范围内只会创建一个 Bean 实例。该 Bean 实例在用户会话范围内共享,仅在 Spring Web 应用程序中有效,适用于与用户会话相关的 Bean。
  • Application:当前 ServletContext 中只存在一个 Bean 实例。仅在 Spring Web 应用程序中有效,该 Bean 实例在整个 ServletContext 范围内共享,适用于应用程序范围内共享的 Bean。
  • WebSocket(Web 套接字):在 WebSocket 范围内只存在一个 Bean 实例。仅在支持 WebSocket 的应用程序中有效,该 Bean 实例在 WebSocket 会话范围内共享,适用于 WebSocket 会话范围内共享的 Bean。
  • Custom scopes(自定义作用域):Spring 允许开发者定义自定义的作用域,通过实现 Scope 接口来创建新的 Bean 作用域。

在 Spring 配置文件中,可以通过标签的scope属性来指定 Bean 的作用域。例如:

XML 复制代码
<bean id="myBean" class="com.example.MyBeanClass" scope="singleton"/>

在 Spring Boot 或基于 Java 的配置中,可以通过@Scope注解来指定 Bean 的作用域。例如:

java 复制代码
@Bean
@Scope("prototype")
public MyBeanClass myBean() {
    return new MyBeanClass();
}

23. Spring容器里存的是什么?

在 Spring 容器中,存储的主要是 Bean 对象

Bean 是 Spring 框架中的基本组件,用于表示应用程序中的各种对象。当应用程序启动时,Spring 容器会根据配置文件或注解的方式创建和管理这些 Bean 对象。Spring 容器会负责创建、初始化、注入依赖以及销毁 Bean 对象。

24. Bean注入和xml注入最终得到了相同的效果,它们在底层是怎样做的?

在 Spring 框架中,基于注解的 Bean 注入(如 @Autowired@Resource )和基于 XML 的依赖注入虽然在配置方式上不同,但在底层最终都通过 Spring 容器的统一机制实现依赖注入。它们的核心流程可以归纳为以下步骤:

阶段 注解注入 XML 注入
配置解析 通过注解处理器扫描类路径,解析 @Component@Autowired 等注解。 解析 XML 文件中的 <bean><property><constructor-arg> 标签。
生成 BeanDefinition 将注解信息转换为 AnnotatedBeanDefinition 将 XML 配置转换为 GenericBeanDefinition
依赖注入 AutowiredAnnotationBeanPostProcessor 等后处理器处理。 在 BeanDefinition 中直接记录属性或构造器参数,由容器直接注入。
最终结果 生成完整的 Bean 实例,完成依赖注入。 生成完整的 Bean 实例,完成依赖注入。

XML 注入

使用 XML 文件进行 Bean 注入时,Spring 在启动时会读取 XML 配置文件,以下是其底层步骤:

  • Bean 定义解析 :Spring 容器通过 XmlBeanDefinitionReader 类解析 XML 配置文件,读取其中的 <bean> 标签以获取 Bean 的定义信息。
  • 注册 Bean 定义 :解析后的 Bean 信息被注册到 BeanDefinitionRegistry(如 DefaultListableBeanFactory )中,包括 Bean 的类、作用域、依赖关系、初始化和销毁方法等。
  • 实例化和依赖注入 :当应用程序请求某个 Bean 时,Spring 容器会根据已经注册的 Bean 定义:
    • 首先,使用反射机制创建该 Bean 的实例。
    • 然后,根据 Bean 定义中的配置,通过 setter 方法、构造函数或方法注入所需的依赖 Bean。

注解注入

使用注解进行 Bean 注入时,Spring 的处理过程如下:

  • 类路径扫描 :当 Spring 容器启动时,它首先会进行类路径扫描,查找带有特定注解(如 @Component@Service@Repository@Controller )的类。
  • 注册 Bean 定义 :找到的类会被注册到 BeanDefinitionRegistry 中,Spring 容器将为其生成 Bean 定义信息。这通常通过 AnnotatedBeanDefinitionReader 类来实现。
  • 依赖注入 :与 XML 注入类似,Spring 在实例化 Bean 时,也会检查字段上是否有 @Autowired@Inject@Resource 注解。如果有,Spring 会根据注解的信息进行依赖注入。

尽管使用的方式不同,但 XML 注入和注解注入在底层的实现机制是相似的,主要体现在以下几个方面:

  • BeanDefinition :无论是 XML 还是注解,最终都会生成 BeanDefinition 对象,并存储在同一个 BeanDefinitionRegistry 中。
  • 后处理器
    • Spring 提供了多个 Bean 后处理器(如 AutowiredAnnotationBeanPostProcessor ),用于处理注解(如 @Autowired )的依赖注入。
    • 对于 XML,Spring 也有相应的后处理器来处理 XML 配置的依赖注入。
  • 依赖查找 :在依赖注入时,Spring 容器会通过 ApplicationContext 中的 BeanFactory 方法来查找和注入依赖,无论是通过 XML 还是注解,都会调用类似的查找方法。

SpringMVC

25. MVC分层介绍一下

MVC 全名是 Model View Controller,是模型 (model)-视图 (view)-控制器 (controller) 的缩写,一种软件设计典范,用一种业务逻辑、数据、界面显示分离的方法组织代码,将业务逻辑聚集到一个部件里面,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑。

  • 视图 (view): 为用户提供使用界面,与用户直接进行交互。
  • 模型 (model): 代表一个存取数据的对象或 JAVA POJO(Plain Old Java Object,简单 java 对象)。它也可以带有逻辑,主要用于承载数据,并对用户提交请求进行计算的模块。模型分为两类,一类称为数据承载 Bean,一类称为业务处理 Bean。所谓数据承载 Bean 是指实体类(如:User 类),专门为用户承载业务数据的;而业务处理 Bean 则是指 Service 或 Dao 对象, 专门用于处理用户提交请求的。
  • 控制器 (controller): 用于将用户请求转发给相应的 Model 进行处理,并根据 Model 的计算结果向用户提供相应响应。它使视图与模型分离。

流程步骤:

  1. 用户通过View页面向服务端提出请求,可以是表单请求、超链接请求、AJAX 请求等;
  2. 服务端Controller 控制器接收到请求后对请求进行解析,找到相应的Model ,对用户请求进行处理Model处理;
  3. 将处理结果再交给Controller(控制器其实只是起到了承上启下的作用);
  4. 根据处理结果找到要作为向客户端发回的响应View页面,页面经渲染后发送给客户端。

26. SpringMVC的处理流程?

Spring MVC 的工作流程如下:

  1. 用户发送请求至前端控制器DispatcherServlet
  2. DispatcherServlet收到请求调用处理器映射器HandlerMapping
  3. 处理器映射器根据请求 url 找到具体的处理器,生成处理器执行链HandlerExecutionChain(包括处理器对象和处理器拦截器) 一并返回给DispatcherServlet
  4. DispatcherServlet根据处理器Handler获取处理器适配器HandlerAdapter执行HandlerAdapter处理一系列的操作,如:参数封装,数据格式转换,数据验证等操作
  5. 执行处理器Handler(Controller,也叫页面控制器)。
  6. Handler执行完成返回ModelAndView
  7. HandlerAdapterHandler执行结果ModelAndView返回到DispatcherServlet
  8. DispatcherServletModelAndView传给ViewReslover视图解析器
  9. ViewReslover解析后返回具体View
  10. DispatcherServletView进行渲染视图(即将模型数据model填充至视图中)。
  11. DispatcherServlet响应用户。

27. Handlermapping 和 Handleradapter有了解吗?

HandlerMapping

  • 作用:HandlerMapping 负责将请求映射到处理器(Controller)。
  • 功能:根据请求的 URL、请求参数等信息,找到处理请求的 Controller。
  • 类型 :Spring 提供了多种 HandlerMapping 实现,如BeanNameUrlHandlerMappingRequestMappingHandlerMapping等。
  • 工作流程:根据请求信息确定要请求的处理器 (Controller)。HandlerMapping 可以根据 URL、请求参数等规则确定对应的处理器。

HandlerAdapter

  • 作用:HandlerAdapter 负责调用处理器 (Controller) 来处理请求。
  • 功能 :处理器 (Controller) 可能有不同的接口类型(Controller 接口、HttpRequestHandler接口等),HandlerAdapter 根据处理器的类型来选择合适的方法来调用处理器。
  • 类型:Spring 提供了多个 HandlerAdapter 实现,用于适配不同类型的处理器。
  • 工作流程:根据处理器的接口类型,选择相应的 HandlerAdapter 来调用处理器。

工作流程

  1. 当客户端发送请求时,HandlerMapping 根据请求信息找到对应的处理器 (Controller)。
  2. HandlerAdapter 根据处理器的类型选择合适的方法来调用处理器。
  3. 处理器执行相应的业务逻辑,生成ModelAndView
  4. HandlerAdapter 将处理器的执行结果包装成ModelAndView
  5. 视图解析器根据ModelAndView找到对应的视图进行渲染。
  6. 将渲染后的视图返回给客户端。

HandlerMapping 和 HandlerAdapter 协同工作,通过将请求映射到处理器,并调用处理器来处理请求,实现了请求处理的流程。它们的灵活性使得在 Spring MVC 中可以支持多种处理器和处理方式,提高了框架的扩展性和适应性。

SpringBoot

28. 为什么使用SpringBoot,SpringBoot优点?

  • 简化开发:Spring Boot 通过提供一系列的开箱即用的组件和自动配置,简化了项目的配置和开发过程,开发人员可以更专注于业务逻辑的实现,而不需要花费过多时间在繁琐的配置上。
  • 快速启动:Spring Boot 提供了快速的应用程序启动方式,可通过内嵌的 Tomcat、Jetty 或 Undertow 等容器快速启动应用程序,无需额外的部署步骤,方便快捷。
  • 自动化配置:Spring Boot 通过自动配置功能,根据项目中的依赖关系和约定俗成的规则来配置应用程序,减少了配置的复杂性,使开发者更容易实现应用的最佳实践。

29. SpringBoot比Spring好在哪里?

  • Spring Boot 提供了自动化配置,大大简化了项目的配置过程。通过约定优于配置的原则,很多常用的配置可以自动完成,开发者可以专注于业务逻辑的实现。
  • Spring Boot 提供了快速的项目启动器,通过引入不同的 Starter,可以快速集成常用的框架和库(如数据库、消息队列、Web 开发等),极大地提高了开发效率。
  • Spring Boot 默认集成了多种内嵌服务器(如 Tomcat、Jetty、Undertow),无需额外配置,即可将应用打包成可执行的 JAR 文件,方便部署和运行。

30. SpringBoot用到哪些设计模式?

  • 代理模式:Spring 的 AOP 通过动态代理实现方法级别的切面增强,有静态和动态两种代理方式,采用动态代理方式。
  • 策略模式:Spring AOP 支持 JDK 和 Cglib 两种动态代理实现方式,通过策略接口和不同策略类,运行时动态选择,其创建一般通过工厂方法实现。
  • 装饰器模式 :Spring 用 TransactionAwareCacheDecorator 解决缓存与数据库事务问题增加对事务的支持。
  • 单例模式 :Spring Bean 默认是单例模式,通过单例注册表(如 HashMap)实现。
  • 简单工厂模式 :Spring 中的 BeanFactory 是简单工厂模式的体现,通过工厂类方法获取 Bean 实例。
  • 工厂方法模式 :Spring 中的 FactoryBean 体现工厂方法模式,为不同产品提供不同工厂。
  • 观察者模式 :Spring 观察者模式包含 Event 事件、Listener 监听者、Publisher 发送者,通过定义事件、监听器和发送者实现,观察者注册在 ApplicationContext 中,消息发送由 ApplicationEventMulticaster 完成。
  • 模板模式:Spring Bean 的创建过程涉及模板模式,体现扩展性,类似 Callback 回调实现方式。
  • 适配器模式 :Spring MVC 中针对不同方式定义的 Controller,利用适配器模式统一函数定义,定义了统一接口 HandlerAdapter 及对应适配器类。

31. 怎么理解SpringBoot中的约定大于配置?

约定大于配置是 Spring Boot 的核心设计理念,它通过预设合理的默认行为和项目规范,大幅减少开发者需要手动配置的步骤,从而提升开发效率和项目标准化程度。

理解 Spring Boot 中的 "约定大于配置" 原则,可以从以下几个方面来解释:

  • 自动化配置 :Spring Boot 提供了大量的自动化配置,通过分析项目的依赖和环境,自动配置应用程序的行为。开发者无需显式地配置每个细节,大部分常用的配置都已经预设好了。例如,引入 spring-boot-starter-web 后,Spring Boot 会自动配置内嵌 Tomcat 和 Spring MVC,无需手动编写 XML。
  • 默认配置:Spring Boot 为诸多方面提供大量默认配置,如连接数据库、设置 Web 服务器、处理日志等。开发人员无需手动配置这些常见内容,框架已做好决策。例如,默认的日志配置可让应用程序快速输出日志信息,无需开发者额外繁琐配置日志级别、输出格式与位置等。
  • 约定的项目结构 :Spring Boot 提倡特定项目结构,通常主应用程序类(含 main 方法)置于根包,控制器类、服务类、数据访问类等分别放在相应子包,如 com.example.demo.controller 放控制器类,com.example.demo.service 放服务类等。此约定使团队成员更易理解项目结构与组织,新成员加入项目时能快速定位各功能代码位置,提升协作效率。

32. SpringBoot的项目结构是怎么样的?

  • 开放接口层:可直接封装 Service 接口暴露成 RPC 接口;通过 Web 封装成 http 接口;网关控制层等。
  • 终端显示层:各个端的模板渲染并执行显示的层。当前主要是 velocity 渲染,JS 渲染,JSP 渲染,移动端展示等。
  • Web 层:主要是对访问控制进行转发,各类基本参数校验,或者不复用的业务简单处理等。
  • Service 层:相对具体的业务逻辑服务层。
  • Manager 层 :通用业务处理层,它有如下特征:
    • 1)对第三方平台封装的层,预处理返回结果及转化异常信息,适配上层接口。
    • 2)对 Service 层通用能力的下沉,如缓存方案、中间件通用处理。
    • 3)与 DAO 层交互,对多个 DAO 的组合复用。
  • DAO 层:数据访问层,与底层 MySQL、Oracle、Hbase、OceanBase 等进行数据交互。
  • 第三方服务:包括其它部门 RPC 服务接口,基础平台,其它公司的 HTTP 接口,如淘宝开放平台、支付宝付款服务、高德地图服务等。
  • 外部接口:外部(应用)数据存储服务提供的接口,多见于数据迁移场景中。

如果从一个用户访问一个网站的情况来看,对应着上面的项目代码结构来分析,可以贯穿整个代码分层:

33. SpringBoot自动装配原理是什么?

什么是自动装配?

SpringBoot 的自动装配原理是基于 Spring Framework 的条件化配置和 @EnableAutoConfiguration 注解实现的。这种机制允许开发者在项目中引入相关的依赖,SpringBoot 将根据这些依赖自动配置应用程序的上下文和功能。

SpringBoot 定义了一套接口规范,这套规范规定:SpringBoot 在启动时会扫描外部引用 jar 包中的 META-INF/spring.factories 文件,将文件中配置的类型信息加载到 Spring 容器(此处涉及到 JVM 类加载机制与 Spring 的容器知识),并执行类中定义的各种操作。对于外部 jar 来说,只需要按照 SpringBoot 定义的标准,就能将自己的功能装置进 SpringBoot。

通俗来讲,自动装配就是通过注解或一些简单的配置就可以在 SpringBoot 的帮助下开启和配置各种功能,比如数据库访问、Web 开发。

SpringBoot 自动装配原理

首先点进 @SpringBootApplication 注解的内部

接下来将逐个解释这些注解的作用:

  • **@Target({ElementType.TYPE}):**该注解指定了这个注解可以用来标记在类上。在这个特定的例子中,这表示该注解用于标记配置类。
  • **@Retention(RetentionPolicy.RUNTIME):**这个注解指定了注解的生命周期,即在运行时保留。这是因为 Spring Boot 在运行时扫描类路径上的注解来实现自动配置,所以这里使用了 RUNTIME 保留策略。
  • **@Documented:**该注解表示这个注解应该被包含在 Java 文档中。它是用于生成文档的标记,使开发者能够看到这个注解的相关信息。
  • **@Inherited:**这个注解指示一个被标注的类型是被继承的。在这个例子中,它表明这个注解可以被继承,如果一个类继承了带有这个注解的类,它也会继承这个注解。
  • @SpringBootConfiguration 这个注解表明这是一个 Spring Boot 配置类。如果点进这个注解内部会发现与标准的 @Configuration 没啥区别,只是为了表明这是一个专门用于 SpringBoot 的配置。
  • **@EnableAutoConfiguration:**这个注解是 Spring Boot 自动装配的核心。它告诉 Spring Boot 启用自动配置机制,根据项目的依赖和配置自动配置应用程序的上下文。通过这个注解,SpringBoot 将尝试根据类路径上的依赖自动配置应用程序。
  • @ComponentScan 这个注解用于配置组件扫描的规则。在这里,它告诉 SpringBoot 在指定的包及其子包中查找组件,这些组件包括被注解的类、@Component 注解的类等。其中的 excludeFilters 参数用于指定排除哪些组件,这里使用了两个自定义的过滤器,分别是 TypeExcludeFilterAutoConfigurationExcludeFilter

**@EnableAutoConfiguration**这个注解是实现自动装配的核心注解

  • @AutoConfigurationPackage,将项目 src 中 main 包下的所有组件注册到容器中,例如标注了Component注解的类等
  • **@Import({AutoConfigurationImportSelector.class}),**是自动装配的核心,接下来分析一下这个注解

AutoConfigurationImportSelector 是 Spring Boot 中一个重要的类,它实现了**ImportSelector**接口,用于实现自动配置的选择和导入。具体来说,它通过分析项目的类路径和条件来决定应该导入哪些自动配置类。

梳理一下,以下是**AutoConfigurationImportSelector**的主要工作:

  • 扫描类路径 :在应用程序启动时,AutoConfigurationImportSelector 会扫描类路径上的 META-INF/spring.factories 文件,这个文件中包含了各种 Spring 配置和扩展的定义。在这里,它会查找所有实现了 AutoConfiguration 接口的类,具体的实现为 getCandidateConfigurations 方法。
  • 条件判断 :对于每一个发现的自动配置类,AutoConfigurationImportSelector 会使用条件判断机制(通常是通过 @ConditionalOnXxx 注解)来确定是否满足导入条件。这些条件可以是配置属性、类是否存在、Bean 是否存在等等。
  • 根据条件导入自动配置类:满足条件的自动配置类将被导入到应用程序的上下文中。这意味着它们会被实例化并应用于应用程序的配置。

34. 说几个启动器(starter)?

  • **spring-boot-starter-web:**这是最常用的起步依赖之一,它包含了 Spring MVC 和 Tomcat 嵌入式服务器,用于快速构建 Web 应用程序。
  • **spring-boot-starter-security:**提供了 Spring Security 的基本配置,帮助开发者快速实现应用的安全性,包括认证和授权功能。
  • mybatis-spring-boot-starter 这个 Starter 是由 MyBatis 团队提供的,用于简化在 Spring Boot 应用中集成 MyBatis 的过程。它自动配置了 MyBatis 的相关组件,包括SqlSessionFactoryMapperScannerConfigurer等,使得开发者能够快速地开始使用 MyBatis 进行数据库操作。
  • spring-boot-starter-data-jpaspring-boot-starter-jdbc 如果使用的是 Java Persistence API (JPA) 进行数据库操作,那么应该使用spring-boot-starter-data-jpa。这个 Starter 包含了 Hibernate 等 JPA 实现以及数据库连接池等必要的库,可以让你轻松地与 MySQL 数据库进行交互。你需要在application.propertiesapplication.yml中配置 MySQL 的连接信息。如果倾向于直接使用 JDBC 而不通过 JPA,那么可以使用spring-boot-starter-jdbc,它提供了基本的 JDBC 支持。
  • **spring-boot-starter-data-redis:**用于集成 Redis 缓存和数据存储服务。这个 Starter 包含了与 Redis 交互所需的客户端(默认是 Jedis 客户端,也可以配置为 Lettuce 客户端),以及 Spring Data Redis 的支持,使得在 Spring Boot 应用中使用 Redis 变得非常便捷。同样地,需要在配置文件中设置 Redis 服务器的连接详情。
  • **spring-boot-starter-test:**包含了单元测试和集成测试所需的库,如 JUnit, Spring Test, AssertJ 等,便于进行测试驱动开发 (TDD)。

35. SpringBoot里面有哪些重要的注解?还有一个配置相关的注解是哪个?

Spring Boot 中一些常用的注解包括:

  • **@SpringBootApplication:**用于标注主应用程序类,标识一个 Spring Boot 应用程序的入口点,同时启用自动配置和组件扫描。
  • **@Controller:**标识控制器类,处理 HTTP 请求。
  • @RestController 结合@Controller@ResponseBody,返回 RESTful 风格的数据。
  • **@Service:**标识服务类,通常用于标记业务逻辑层。
  • **@Repository:**标识数据访问组件,通常用于标记数据访问层。
  • **@Component:**通用的 Spring 组件注解,表示一个受 Spring 管理的组件。
  • **@Autowired:**用于自动装配 Spring Bean。
  • **@Value:**用于注入配置属性值。
  • **@RequestMapping:**用于映射 HTTP 请求路径到 Controller 的处理方法。
  • @GetMapping@PostMapping@PutMapping@DeleteMapping 简化@RequestMapping的 GET、POST、PUT 和 DELETE 请求。

另外,一个与配置相关的重要注解是:

  • @Configuration 用于指定一个类为配置类,其中定义的 bean 会被 Spring 容器管理。通常与@Bean配合使用,@Bean用于声明一个 Bean 实例,由 Spring 容器进行管理。

36. SpringBoot怎么开启事务?

在 Spring Boot 中开启事务非常简单,只需在服务层的方法上添加 @Transactional 注解即可。

例如,假设我们有一个 UserService 接口,其中有一个保存用户的方法 saveUser()

java 复制代码
public interface UserService {
    void saveUser(User user);
}

我们希望在这个方法中开启事务,只需在该方法上添加 @Transactional 注解,如下所示:

java 复制代码
public class UserServiceImpl implements UserService {

    @Autowired
    private UserRepository userRepository;

    @Override
    @Transactional
    public void saveUser(User user) {
        userRepository.save(user);
    }
}

这样,当调用 saveUser() 方法时,Spring 就会自动为该方法开启一个事务。如果方法执行成功,事务会自动提交;如果方法执行失败,事务会自动回滚。

XML 复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

37. Springboot怎么做到导入就可以直接使用的?

这个主要依赖于自动配置、起步依赖和条件注解等特性。

起步依赖

起步依赖是一种特殊的 Maven 或 Gradle 依赖,它将项目所需的一系列依赖打包在一起。例如,spring-boot-starter-web 这个起步依赖就包含了 Spring Web MVC、Tomcat 等构建 Web 应用所需的核心依赖。

开发者只需在项目中添加一个起步依赖,Maven 或 Gradle 就会自动下载并管理与之关联的所有依赖,避免了手动添加大量依赖的繁琐过程。

比如,在 pom.xml 中添加 spring-boot-starter-web 依赖:

XML 复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

自动配置

Spring Boot 的自动配置机制会根据类路径下的依赖和开发者的配置,自动创建和配置应用所需的 Bean。它通过 @EnableAutoConfiguration 注解启用,该注解会触发 Spring Boot 去查找 META-INF/spring.factories 文件。

spring.factories 文件中定义了一系列自动配置类,Spring Boot 会根据当前项目的依赖情况,选择合适的自动配置类进行加载。例如,如果项目中包含 spring-boot-starter-web 依赖,Spring Boot 会加载 WebMvcAutoConfiguration 类,该类会自动配置 Spring MVC 的相关组件,如 DispatcherServlet、视图解析器等。

开发者可以通过自定义配置来覆盖自动配置的默认行为。如果开发者在 application.propertiesapplication.yml 中定义了特定的配置,或者在代码中定义了同名的 Bean,Spring Boot 会优先使用开发者的配置。

条件注解

条件注解用于控制 Bean 的创建和加载,只有在满足特定条件时,才会创建相应的 Bean。Spring Boot 的自动配置类中广泛使用了条件注解,如 @ConditionalOnClass@ConditionalOnMissingBean 等。

比如,@ConditionalOnClass 表示只有当类路径中存在指定的类时,才会创建该 Bean。例如,在 WebMvcAutoConfiguration 类中,可能会有如下代码:

java 复制代码
@Configuration
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
public class WebMvcAutoConfiguration {
    // 配置相关的 Bean
}

这段代码表示只有当类路径中存在 ServletDispatcherServletWebMvcConfigurer 类时,才会加载 WebMvcAutoConfiguration 类中的配置。

38. SpringBoot 过滤器和拦截器说一下?

在 Spring Boot 中,过滤器(Filter)和拦截器(Interceptor)是用于处理请求和响应的两种不同机制。

特性 过滤器(Filter) 拦截器(Interceptor)
规范 / 框架 Servlet 规范(javax.servlet.Filter Spring MVC 框架(org.springframework.web.servlet.HandlerInterceptor
作用范围 全局(所有请求、静态资源) Controller 层(仅拦截 Spring 管理的请求)
执行顺序 在 Servlet 之前执行 在 DispatcherServlet 之后、Controller 方法前后执行
依赖注入支持 无法直接注入 Spring Bean(需间接获取) 支持自动注入 Spring Bean
触发时机 doFilter() 在请求前 / 响应后被调用 preHandlepostHandleafterCompletion 分阶段触发
适用场景 全局请求处理(编码、日志、安全) 业务逻辑相关的处理(权限、参数校验)

过滤器是 Java Servlet 规范中的一部分,它可以对进入 Servlet 容器的请求和响应进行预处理和后处理。

过滤器通过实现 javax.servlet.Filter 接口,并重写其中的 initdoFilterdestroy 方法来完成相应的逻辑。当请求进入 Servlet 容器时,会按照配置的顺序依次经过各个过滤器,然后再到达目标 Servlet 或控制器;响应返回时,也会按照相反的顺序再次经过这些过滤器。

拦截器是 Spring 框架提供的一种机制,它可以对控制器方法的执行进行拦截。拦截器通过实现 org.springframework.web.servlet.HandlerInterceptor 接口,并重写其中的 preHandlepostHandleafterCompletion 方法来完成相应的逻辑。当请求到达控制器时,会先经过拦截器的 preHandle 方法,如果该方法返回 true,则继续执行后续的控制器方法和其他拦截器;在控制器方法执行完成后,会调用拦截器的 postHandle 方法;最后,在请求处理完成后,会调用拦截器的 afterCompletion 方法。

过滤器和拦截器的区别如下:

  • 所属规范:过滤器是 Java Servlet 规范的一部分,而拦截器是 Spring 框架提供的机制。
  • 执行顺序:过滤器在请求进入 Servlet 容器后,在到达目标 Servlet 或控制器之前执行;拦截器在请求到达控制器之后,在控制器方法执行前后执行。
  • 使用范围:过滤器可以对所有类型的请求进行过滤,包括静态资源请求;拦截器只能对 Spring MVC 控制器的请求进行拦截。
  • 功能特性:过滤器主要用于对请求和响应进行预处理和后处理,如字符编码处理、请求日志记录等;拦截器可以更细粒度地控制控制器方法的执行,如权限验证、性能监控等。

Mybatis

39. 与传统的JDBC相比,MyBatis的优点?

  • 基于 SQL 语句编程,相当灵活,不会对应用程序或者数据库的现有设计造成任何影响,SQL 写在 XML 里,解除 sql 与程序代码的耦合,便于统一管理;提供 XML 标签,支持编写动态 SQL 语句,并可重用。
  • 与 JDBC 相比,减少了 50% 以上的代码量,消除了 JDBC 大量冗余的代码,不需要手动开关连接;
  • 很好的与各种数据库兼容,因为 MyBatis 使用 JDBC 来连接数据库,所以只要 JDBC 支持的数据库 MyBatis 都支持。
  • 能够与 Spring 很好的集成,开发效率高
  • 提供映射标签,支持对象与数据库的 ORM 字段关系映射;提供对象关系映射标签,支持对象关系组件维护。

40. MyBatis觉得在哪方面做的比较好?

MyBatis 在 SQL 灵活性动态 SQL 支持结果集映射与 Spring 整合方面表现卓越,尤其适合重视 SQL 可控性的项目。

  • SQL 与代码解耦,灵活可控:MyBatis 允许开发者直接编写和优化 SQL,相比全自动 ORM(如 Hibernate),MyBatis 让开发者明确知道每条 SQL 的执行逻辑,便于性能调优。
java 复制代码
<!-- 示例:XML 中定义 SQL -->
<select id="findUserWithRole" resultMap="userRoleMap">
    SELECT u.*, r.role_name
    FROM user u
    LEFT JOIN user_role ur ON u.id = ur.user_id
    LEFT JOIN role r ON ur.role_id = r.id
    WHERE u.id = #{userId}
</select>
  • 动态 SQL 的强大支持 :比如可以动态拼接 SQL,通过 <if><choose><foreach> 等标签动态生成 SQL,避免 Java 代码中繁琐的字符串拼接。
java 复制代码
<select id="searchUsers" resultType="User">
    SELECT * FROM user
    <where>
        <if test="name != null">AND name LIKE #{name}</if>
        <if test="status != null">AND status = #{status}</if>
    </where>
</select>
  • 自动映射与自定义映射结合:自动将查询结果字段名与对象属性名匹配(如驼峰转换)。
java 复制代码
<resultMap id="userRoleMap" type="User">
    <id property="id" column="user_id"/>
    <result property="name" column="user_name"/>
    <collection property="roles" ofType="Role">
        <result property="roleName" column="role_name"/>
    </collection>
</resultMap>
  • 插件扩展机制:可编写插件拦截 SQL 执行过程,实现分页、性能监控、SQL 改写等通用逻辑。
java 复制代码
@Intercepts({
    @Signature(type=Executor.class, method="query", args={...})
})
public class PaginationPlugin implements Interceptor {
    // 实现分页逻辑
}
  • 与 Spring 生态无缝集成 :通过 @MapperScan 快速扫描 Mapper 接口,结合 Spring 事务管理,配置简洁高效。
java 复制代码
@Configuration
@MapperScan("com.example.mapper")
public class MyBatisConfig {
    // 数据源和 SqlSessionFactory 配置
}

41. 还记得JDBC连接数据库的步骤吗?

使用 Java JDBC 连接数据库的一般步骤如下:

  1. 加载数据库驱动程序 :在使用 JDBC 连接数据库之前,需要加载相应的数据库驱动程序。可以通过 Class.forName("com.mysql.jdbc.Driver") 来加载 MySQL 数据库的驱动程序。不同数据库的驱动类名会有所不同。
  2. 建立数据库连接 :使用 DriverManager 类的 getConnection(url, username, password) 方法来连接数据库,其中 url 是数据库的连接字符串(包括数据库类型、主机、端口等)、username 是数据库用户名,password 是密码。
  3. 创建 Statement 对象 :通过 Connection 对象的 createStatement() 方法创建一个 Statement 对象,用于执行 SQL 查询或更新操作。
  4. 执行 SQL 查询或更新操作 :使用 Statement 对象的 executeQuery(sql) 方法来执行 SELECT 查询操作,或者使用 executeUpdate(sql) 方法来执行 INSERT、UPDATE 或 DELETE 操作。
  5. 处理查询结果 :如果是 SELECT 查询操作,通过 ResultSet 对象来处理查询结果。可以使用 ResultSetnext() 方法遍历查询结果集,然后通过 getXXX() 方法获取各个字段的值。
  6. 关闭连接 :在完成数据库操作后,需要逐级关闭数据库连接相关对象,即先关闭 ResultSet,再关闭 Statement,最后关闭 Connection

以下是一个简单的示例代码:

java 复制代码
import java.sql.*;

public class Main {
    public static void main(String[] args) {
        try {
            // 加载数据库驱动程序
            Class.forName("com.mysql.cj.jdbc.Driver");

            // 建立数据库连接
            Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydatabase","username","password");

            // 创建 Statement 对象
            Statement statement = connection.createStatement();

            // 执行 SQL 查询
            ResultSet resultSet = statement.executeQuery("SELECT * FROM mytable");

            // 处理查询结果
            while (resultSet.next()) {
                // 处理每一行数据
            }

            // 关闭资源
            resultSet.close();
            statement.close();
            connection.close();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

42. 如果项目中要用到原生的MyBatis去查询,该怎样写?

步骤概述:

  1. 配置 MyBatis: 在项目中配置 MyBatis 的数据源、SQL 映射文件等。
  2. 创建实体类: 创建用于映射数据库表的实体类。
  3. 编写 SQL 映射文件: 创建 XML 文件,定义 SQL 语句和映射关系。
  4. 编写 DAO 接口: 创建 DAO 接口,定义数据库操作的方法。
  5. 编写具体的 SQL 查询语句: 在 DAO 接口中定义查询方法,并在 XML 文件中编写对应的 SQL 语句。
  6. 调用查询方法: 在服务层或控制层调用 DAO 接口中的方法进行查询。

43. Mybatis里的 # 和 $ 的区别?

  • Mybatis 在处理 #{} 时, 会创建预编译的 SQL 语句,将 SQL 中的 #{} 替换为 ? 号,在执行 SQL 时会为预编译 SQL 中的占位符(?)赋值,调用 PreparedStatementset 方法来赋值,预编译的 SQL 语句执行效率高,并且**可以防止 SQL 注入,**提供更高的安全性,适合传递参数值。
  • Mybatis 在处理 ${} 时, 只是创建普通的 SQL 语句,然后在执行 SQL 语句时 MyBatis 将参数直接拼入到 SQL 里,**不能防止 SQL 注入,**因为参数直接拼接到 SQL 语句中,如果参数未经过验证、过滤,可能会导致安全问题。

44. MybatisPlus和Mybatis的区别?

MybatisPlus 是一个基于 MyBatis 的增强工具库,旨在简化开发并提高效率。以下是 MybatisPlus 和 MyBatis 之间的一些主要区别:

  • CRUD 操作 :MybatisPlus 通过继承BaseMapper接口,提供了一系列内置的快捷方法,使得 CRUD 操作更加简单,无需编写重复的 SQL 语句。
  • 代码生成器:MybatisPlus 提供了代码生成器功能,可以根据数据库表结构自动生成实体类、Mapper 接口以及 XML 映射文件,减少了手动编写的工作量。
  • 通用方法封装:MybatisPlus 封装了许多常用的方法,如条件构造器、排序、分页查询等,简化了开发过程,提高了开发效率。
  • 分页插件:MybatisPlus 内置了分页插件,支持各种数据库的分页查询,开发者可以轻松实现分页功能,而在传统的 MyBatis 中,需要开发者自己手动实现分页逻辑。
  • 多租户支持:MybatisPlus 提供了多租户的支持,可以轻松实现多租户数据隔离的功能。
  • 注解支持:MybatisPlus 引入了更多的注解支持,使得开发者可以通过注解来配置实体与数据库表之间的映射关系,减少了 XML 配置文件的编写。

45. MyBatis运用了哪些常见的设计模式?

  • 建造者模式(Builder) ,如:SqlSessionFactoryBuilderXMLConfigBuilderXMLMapperBuilderXMLStatementBuilderCacheBuilder等;
  • 工厂模式 ,如:SqlSessionFactoryObjectFactoryMapperProxyFactory
  • 单例模式 ,例如ErrorContextLogFactory
  • 代理模式 ,Mybatis 实现的核心,比如MapperProxyConnectionLogger,用的 jdk 的动态代理;还有executor.loader包使用了cglib或者javassist达到延迟加载的效果;
  • 组合模式 ,例如SqlNode和各个子类ChooseSqlNode等;
  • 模板方法模式 ,例如BaseExecutorSimpleExecutor,还有BaseTypeHandler和所有的子类例如IntegerTypeHandler
  • 适配器模式 ,例如Log的 Mybatis 接口和它对jdbclog4j等各种日志框架的适配实现;
  • 装饰者模式 ,例如Cache包中的cache.decorators子包中等各个装饰者的实现;
  • 迭代器模式 ,例如迭代器模式PropertyTokenizer
相关推荐
Dcs2 小时前
用不到 1000 行 Go 实现 BaaS,Pennybase 是怎么做到的?
java
Cyanto4 小时前
Spring注解IoC与JUnit整合实战
java·开发语言·spring·mybatis
qq_433888934 小时前
Junit多线程的坑
java·spring·junit
写不出来就跑路4 小时前
WebClient与HTTPInterface远程调用对比
java·开发语言·后端·spring·springboot
Cyanto4 小时前
深入MyBatis:CRUD操作与高级查询实战
java·数据库·mybatis
麦兜*5 小时前
Spring Boot 集成Reactive Web 性能优化全栈技术方案,包含底层原理、压测方法论、参数调优
java·前端·spring boot·spring·spring cloud·性能优化·maven
天上掉下来个程小白5 小时前
MybatisPlus-06.核心功能-自定义SQL
java·spring boot·后端·sql·微服务·mybatisplus
知了一笑5 小时前
独立开发第二周:构建、执行、规划
java·前端·后端
今天背单词了吗9806 小时前
算法学习笔记:17.蒙特卡洛算法 ——从原理到实战,涵盖 LeetCode 与考研 408 例题
java·笔记·考研·算法·蒙特卡洛算法