【面试题】Spring/SpringBoot部分[2025/1/13 ~ 2025/1/19]

devtools/2025/1/19 22:49:38/

Spring/SpringBoot部分[2025/1/13 ~ 2025/1/19]

  • 8. 什么是 Spring IOC/Di?
  • 9. Spring AOP默认用的是什么动态代理,两者的区别?
  • 10. 什么是 AOP?
  • 11. 看过源码吗?说下 Spring 由哪些重要的模块组成?
  • 12. 什么是循环依赖(常问)?
  • 13. 为什么 Spring 循环依赖需要三级缓存,二级不够吗?
  • 14. 说下 Spring Bean 的生命周期?
  • 15. Spring MVC 具体的工作原理?

我的博客地址

8. 什么是 Spring IOC/Di?

  1. 什么是 Spring IOC
    • 定义: Spring IOC(Inversion of Control,控制反转)是 Spring 框架的核心概念之一。它是通过依赖注入(Dependency Injection) 实现的。IOC 让对象的创建与管理职责由容器负责,而不是由对象自身控制。
      • 控制: IOC 容器控制对象的创建,IOC 容器会根据配置文件来创建对象,在对象的生命周期不同时期根据不同配置进行对象的创建和改造。
      • 反转: 创建对象且注入依赖对象的这个动作, 即创建对象A需要对象B, 这时候需要程序员手动创建, 反转的作用是将这一动作由 IOC 容器触发.
  2. 什么是 DI
    DI(Dependency Injection,依赖注入)普遍认为是 Spring 框架中用于实现控制反转(IOC) 的一种机制。DI 的核心思想是由容器负责对象的依赖注入,而不是由对象自行创建或查找依赖对象。

9. Spring AOP默认用的是什么动态代理,两者的区别?

  1. 默认代理方式
    • Spring Framework 默认使用的动态代理是 JDK 动态代理
    • SpringBoot 2.x 版本的默认动态代理是 CGLIB。
  2. 区别
    1. 代理方式
      • JDK 动态代理:基于接口实现,通过 java.lang.reflect.Proxy 动态生成代理类。
      • CGLIB 动态代理:基于类继承,通过字节码技术生成目标类的子类,来实现对目标方法的代理。
    2. 使用场景:
      • JDK 动态代理:推荐用于代理接口的场景,适合代理的类实现了接口。
      • CGLIB 动态代理:适合没有接口的类,或需要代理具体类中的非接口方法的场景。由于基于继承实现,不能代理 final 类和 final 方法。
  3. 实现
  • JDK 动态代理
  1. 简单 JDK 动态代理示例
java">// 接口
public interface Service {void perform();
}// 需要被代理的实现类
public class ServiceImpl implements Service {@Overridepublic void perform() {System.out.println("mianshiya.com");}
}
  1. JDK 动态代理处理类:
java">import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;public class ServiceInvocationHandler implements InvocationHandler {private final Object target;public ServiceInvocationHandler(Object target) {this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("Before method invoke");Object result = method.invoke(target, args);System.out.println("After method invoke");return result;}
}
  1. 创建并使用动态代理对象:
java">import java.lang.reflect.Proxy;public class DynamicProxyDemo {public static void main(String[] args) {Service target = new ServiceImpl();Service proxy = (Service) Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),new ServiceInvocationHandler(target));proxy.perform();}
}
  • CGLIB 动态代理
  1. 服务示例:
java">public class Service {public void perform() {System.out.println("mianshiya.com");}
}
  1. CGLIB 动态代理处理类:
java">import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;import java.lang.reflect.Method;public class ServiceMethodInterceptor implements MethodInterceptor {@Overridepublic Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {System.out.println("Before method invoke");Object result = proxy.invokeSuper(obj, args);System.out.println("After method invoke");return result;}
}
  1. 创建并使用动态代理对象:
java">import net.sf.cglib.proxy.Enhancer;public class CglibDynamicProxyDemo {public static void main(String[] args) {Enhancer enhancer = new Enhancer();enhancer.setSuperclass(Service.class);enhancer.setCallback(new ServiceMethodInterceptor());Service proxy = (Service) enhancer.create();proxy.perform();}
}

10. 什么是 AOP?

AOP(Aspect-Oriented Programming,面向切面编程) 是一种编程范式, 通过将通用代码模块化, 并使用切面应用到用到业务多个地方, 避免代码重复, 从而达到不同业务分离的目的
主要组成部分:AOP 包括几个关键概念:切面(Aspect)、连接点(Join Point)、通知(Advice)、切入点(Pointcut)和织入(Weaving)。

  1. 切面(Aspect):切面是一个模块,包含跨领域的关注点,比如日志、事务等。它可以包含多个通知(Advice)来定义在何时何地应用特定的逻辑。
  2. 连接点(Join Point):连接点是程序执行中的一个特定位置,例如方法调用或异常抛出。AOP 允许在这些点上插入切面逻辑。
  3. 通知(Advice):通知是定义在连接点执行的操作。
    • 前置通知(Before):在方法执行之前执行的操作。
    • 后置通知(After):在方法执行之后执行的操作。
    • 环绕通知(Around):在方法执行前后都可以执行的操作,可以控制方法是否执行。
    • 异常通知(AfterThrowing):在方法抛出异常后执行的操作。
    • 返回通知(AfterReturning):在方法成功返回后执行的操作。
  4. 切入点(Pointcut):切入点定义了在何处应用通知,通常是通过表达式来匹配方法或类。例如,可以定义某个包下所有方法为切入点。
  5. 织入(Weaving):织入是将切面应用到目标对象的过程。可以在编译时、类加载时或运行时进行织入。

11. 看过源码吗?说下 Spring 由哪些重要的模块组成?

  1. Core Container(核心容器):
    • Spring Core:提供了依赖注入(Dependency Injection, DI)和控制反转(Inversion of Control, IoC)的实现,所有其他Spring模块的基础,别的模块都会依赖此模块。
    • Spring Beans:负责管理Bean的定义和生命周期。通过IoC容器完成Bean的创建、依赖注入、初始化、销毁等操作。
    • Spring Context:基于Core和Beans的高级容器,提供了类似JNDI的上下文功能,还包含了国际化、事件传播、资源访问等功能。
    • Spring Expression Language(SpEL):一个强大的表达式语言,用于在运行时查询和操作对象的值。
  2. AOP(面向切面编程):
    • Spring AOP:提供面向切面编程的功能,可以在方法执行前后或抛出异常时动态插入额外的逻辑,比如日志记录、权限验证、事务管理等。
  3. Data Access(数据访问):
    • Spring JDBC:简化了原生JDBC的操作,提供模板方法来管理连接、资源的释放和异常处理。
    • Spring ORM:支持与主流ORM框架(如Hibernate、JPA、MyBatis等)集成,简化持久层开发。
    • Spring Transaction(事务管理):提供声明式和编程式的事务管理机制,与数据库操作密切结合。
  4. Web层
    • Spring Web:提供基础的Web开发支持,包括Servlet API的集成,适用于构建MVC架构。
    • Spring MVC:实现了Model-View-Controller(MVC)模式的框架,用于构建基于HTTP请求的Web应用。它是一个常用的模块,支持注解驱动的Web开发。
    • Spring WebFlux:提供基于Reactive Streams的响应式编程模型,专为高并发的异步非阻塞请求设计。
  5. 除了核心模块外
    • Spring Batch:用于批处理的框架,支持大规模数据的处理与分块执行。
    • Spring Integration:提供消息驱动的应用程序集成方案,适用于构建企业集成架构(EAI)。
    • Spring Cloud:用于构建微服务架构的模块集合,支持分布式系统中的服务注册、配置管理、服务调用等功能。

12. 什么是循环依赖(常问)?

循环依赖(Circular Dependency)是指两个或多个模块、类、组件之间相互依赖,形成一个闭环。简而言之,模块A依赖于模块B,而模块B又依赖于模块A,这会导致依赖链的循环,无法确定加载或初始化的顺序。

  1. spring循环依赖的判决办法: 使用三级缓存来解决了循环依赖(提前暴露未完全创建完毕的 Bean)

    • 一级缓存(singletonObjects):这是一个ConcurrentHashMap,用于存储完全初始化完成的单例bean。当bean创建完成,并且其属性都填充完毕、初始化方法也执行完成后,就会被放入这个缓存中。
    • 二级缓存(earlySingletonObjects):这是一个ConcurrentHashMap,用于存储早期暴露的单例bean。在bean的创建过程中,如果需要提前暴露这个bean(例如,解决循环依赖),就会先将其放入这个缓存中。此时,bean没有初始化完成, 但是已经实例的bean(bean可能还没有完全初始化完成,属性可能还没有全部填充)。
    • 三级缓存(Singleton Factories Map): 这是一个ConcurrentHashMap,用于存储bean的ObjectFactory(特别是为了支持AOP代理对象的创建)。ObjectFactory是一个工厂接口,可以用于创建bean。当bean开始创建时,会先将一个ObjectFactory放入这个缓存中。这个工厂可以用于后续获取bean的实例,无论bean是否已经完全创建完成。
  2. 解决循环依赖的步骤

    1. 创建bean实例并放入三级缓存:开始创建bean时,先创建ObjectFactory并放入三级缓存singletonFactories。
    2. 提前暴露bean实例到二级缓存:在属性填充前,若检测到可能的循环依赖,会通过ObjectFactory提前创建bean的原始实例,并放入二级缓存earlySingletonObjects。
    3. 填充属性和调用初始化方法:继续创建bean,填充属性时,会先从一级缓存singletonObjects获取依赖bean,再从二级缓存earlySingletonObjects获取。属性填充完后,调用初始化方法。
    4. 放入一级缓存并清理其他缓存:初始化方法执行完后,将bean放入一级缓存singletonObjects,并从二级、三级缓存中移除相关数据。

13. 为什么 Spring 循环依赖需要三级缓存,二级不够吗?

Spring循环依赖需要三级缓存而非二级缓存,原因如下:

  • 三级缓存分工明确:三级缓存(singletonFactories)存储ObjectFactory,是创建bean的起点;二级缓存(earlySingletonObjects)存储提前暴露的原始bean实例,用于打破循环依赖;一级缓存(singletonObjects)存储完全初始化完成的bean,供后续稳定使用。
  • 二级缓存无法单独解决问题:二级缓存中的原始bean实例需要依赖三级缓存中的ObjectFactory来创建,且二级缓存中的bean未完全初始化,需一级缓存提供最终可用的bean。
  • 三级缓存相互配合:只有三级缓存相互配合,才能在bean创建过程中灵活应对循环依赖,确保bean正确、完整地创建并投入使用。

14. 说下 Spring Bean 的生命周期?

  1. 实例化:Spring 容器根据配置文件或注解实例化 Bean 对象。
  2. 属性注入:Spring 将依赖(通过构造器、setter 方法或字段注入)注入到 Bean 实例中。
  3. 执行aware:如果 Bean 实现了 BeanNameAware 等 aware 接口,则执行 aware 注入。
  4. 初始化前(BeanPostProcessor):在 Bean 初始化之前,可以通过 BeanPostProcessor 接口对 Bean 进行一些额外的处理。
  5. 初始化:调用 InitializingBean 接口的 afterPropertiesSet() 方法或通过 init-method 属性指定的初始化方法。
  6. 初始化后(BeanPostProcessor):在 Bean 初始化后,可以通过 BeanPostProcessor 进行进一步的处理。
  7. 使用 Bean:Bean 已经初始化完成,可以被容器中的其他 Bean 使用。
  8. 销毁:当容器关闭时,Spring 调用 DisposableBean 接口的 destroy() 方法或通过 destroy-method 属性指定的销毁方法。

15. Spring MVC 具体的工作原理?

  1. 客户端请求:用户通过浏览器发送 HTTP 请求,所有请求都被 DispatcherServlet 接收。
  2. 请求映射:DispatcherServlet 根据配置的处理器映射器(HandlerMapping)查找与请求 URL 对应的控制器(Controller)。
  3. 调用控制器方法:找到控制器后,DispatcherServlet 将请求转发给对应的控制器方法进行处理。控制器方法处理业务逻辑后,通常返回一个 ModelAndView 对象,包含数据模型和视图名称。
  4. 视图解析:DispatcherServlet 根据控制器返回的视图名称,使用视图解析器(ViewResolver)将逻辑视图名称解析为实际的视图(如 JSP、Thymeleaf 等)。
  5. 视图渲染返回:视图渲染引擎根据数据模型渲染视图,并将生成的 HTML 响应返回给客户端

备注: Spring MVC 还支持返回 JSON 数据响应,用于构建 RESTful Web 服务,因此上述视图解析部分, 如果是 JSON 响应,则 Spring MVC 会将对象序列化为 JSON,后续返回给客户端。


http://www.ppmy.cn/devtools/151938.html

相关文章

自己搭建远程桌面服务器-RustDesk(小白版)

1.RustDesk简介 此软件主要功能为远程各种设备(其中包括Windows、macOS、Linux、iOS、Android、Web等) 支持文件传输(可直接拷贝远程电脑的文件,类似向日葵的远程文件) 支持内网穿透(支持端口映射&#…

1.18学习记录

re basectf2024 ez_maze 先查壳发现是没壳是64位文件,用ida看看 找到主调函数,查看伪C int __fastcall main(int argc, const char **argv, const char **envp) {int v3; // eaxchar v5[32]; // [rsp20h] [rbp-60h] BYREF__int16 v6; // [rsp40h] [rb…

Tidb集群升级到8.5.0过程中服务器遇到的坑

TiDB 集群升级到8.5.0踩坑记:从 GLIBC_2.15 升级到 GLIBC_2.28YUM 仓库问题的全面解决 1. 引言 作为部门的负责人,我常常觉得自己是个“救火队员”。昨天 TiDB 集群又出问题了:查询卡顿、响应时间变长,重启之后问题依旧。临近下班…

机器学习中的方差与偏差

文章目录 方差与偏差1.1 数据1.1.1 数据的分布1.1.2 拟合 1.2 方差与偏差1.2.1 泛化误差的拆分1.2.2 理解方差偏差 1.3 方差-偏差trade-off1.3.1 方差-偏差trade-off1.3.2 方差与偏差诊断 1.4 降低策略1.4.1 噪声1.4.2 高偏差1.4.3 高方差 方差与偏差 1.1 数据 1.1.1 数据的分…

汽车免拆诊断案例 | 2007 款法拉利 599 GTB 车发动机故障灯异常点亮

故障现象  一辆2007款法拉利599 GTB车,搭载6.0 L V12自然吸气发动机(图1),累计行驶里程约为6万km。该车因发动机故障灯异常点亮进厂检修。 图1 发动机的布置 故障诊断 接车后试车,发动机怠速轻微抖动,…

生产环境中常用的设计模式

生产环境中常用的设计模式 设计模式目的使用场景示例单例模式保证一个类仅有一个实例,并提供一个访问它的全局访问点- 日志记录器- 配置管理器工厂方法模式定义一个创建对象的接口,让子类决定实例化哪个类- 各种工厂类(如视频游戏工厂模式创…

C#高级:用Csharp操作鼠标和键盘

一、winform 1.实时获取鼠标位置 public Form1() {InitializeComponent();InitialTime(); }private void InitialTime() {// 初始化 Timer 控件var timer new System.Windows.Forms.Timer();timer.Interval 100; // 设置为 100 毫秒,即每 0.1 秒更新一次timer.…

《机器学习》——SVD(奇异分解)降维

文章目录 SVD基本定义SVD降维的步骤SVD降维使用场景SVD 降维的优缺点SVD降维实例导入所需库定义SVD降维函数导入图像处理图像处理图像打印降维结果并显示处理后两个图像的对比图 SVD基本定义 简单来说就是,通过SVD(奇异值分解)对矩阵数据进行…