当前位置: 首页 > news >正文

Spring 6 源码深度掘金:66+核心原理与高频面试攻坚指南

一、Core & IoC容器(19题)

Spring 6 中的 AOT(Ahead-of-Time)编译原理

  1. 编译时机: 在 应用构建阶段(而非运行时)进行分析和预编译。
  2. 输入分析:
    • 扫描应用代码(@Configuration类、@Bean方法等)。
    • 推断Spring应用上下文的结构(Bean定义、依赖关系、配置条件)。
    • 识别运行时必需的动态特性(如代理、反射、资源加载)。
  3. 预生成:
    • 根据分析结果,预先生成 必要的、高效的、可预测的 Java代码(或字节码)。
    • 替代传统Spring在启动时进行的运行时字节码生成(如CGLIB/AOP代理)、类路径扫描、配置解析和Bean定义推导。
  4. 输出: 生成优化的 “应用上下文初始化器”代码,直接构建预解析好的Bean定义,绕过运行时的反射和动态处理。
  5. 目标:
    • 显著减少启动时间: 消除了启动时的动态处理开销。
    • 降低内存占用: 减少运行时代码生成和元数据占用。
    • 原生镜像支持基石: 为使用GraalVM生成轻量级、快速启动的原生可执行文件提供必需的静态分析信息和预生成代码。

总结: Spring AOT 在构建期静态分析应用,预生成高效的初始化代码,替代耗时的运行时动态处理,从而优化启动性能和资源消耗,并为原生编译铺路。

Spring框架的核心功能有哪些?

:IoC(控制反转)和AOP(面向切面编程)。

解释IoC和DI,它们之间的关系是什么?

:IoC是设计思想(控制权反转),DI是实现方式(依赖注入)。Spring通过DI实现IoC。

BeanFactory和ApplicationContext的区别?

:ApplicationContext是BeanFactory的子接口,提供更多企业级功能(如事件发布、国际化等),且默认预加载单例Bean。

特性BeanFactoryApplicationContext
Bean加载方式懒加载启动时预加载单例Bean
国际化支持✅ (MessageSource)
事件发布机制✅ (ApplicationEventPublisher)
AOP集成需手动配置内置支持

Spring Bean的作用域?

  • Singleton(全局单例,无状态服务)
    • 范围:整个IoC容器。
    • 生命周期:容器启动时创建(如果设置为懒加载,则在第一次使用时创建),容器关闭时销毁。
    • 适用场景:无状态的Bean,比如配置类、工具类、服务类等。
  • Prototype(原型)
    • 范围:每次获取时创建新实例。
    • 生命周期:创建后由调用者负责管理,容器不负责销毁。因此,要注意资源释放问题。
    • 适用场景:有状态的Bean,比如每次请求需要独立状态的Bean。
  • Request(请求)
    • 范围:一次HTTP请求。
    • 生命周期:请求开始创建,请求结束销毁。
    • 适用场景:存储请求相关的数据,比如表单数据、请求参数等。
  • Session(会话)
    • 范围:一个用户会话(HTTP Session)。
    • 生命周期:会话创建时创建,会话超时或销毁时销毁。
    • 适用场景:用户会话相关的数据,比如用户登录信息、购物车等。
  • Application(应用)
    • 范围:整个Web应用(ServletContext)。
    • 生命周期:Web应用启动时创建,应用停止时销毁。
    • 与Singleton区别:Singleton是Spring容器级别的单例,而Application是ServletContext级别的单例。在同一个ServletContext中,即使是多个Spring容器(如父子容器)也可能会有多个Application作用域的Bean实例?实际上,通常一个Web应用只有一个Spring容器,所以两者区别不大。但在某些特定场景,比如多个DispatcherServlet,每个DispatcherServlet有自己的容器,但共享同一个ServletContext,此时Application作用域的Bean在ServletContext中是唯一的。
  • WebSocket
    • 范围:一个WebSocket会话。
    • 生命周期:WebSocket会话开始时创建,结束时销毁。
    • 适用场景:WebSocket通信过程中保存状态。

如何配置Bean的作用域?

答:XML中scope属性,或注解@Scope("prototype")

Bean的生命周期详细步骤?

:实例化→属性填充→BeanNameAware→BeanFactoryAware→ApplicationContextAware→BeanPostProcessor前置处理→@PostConstruct→InitializingBean→自定义init方法→BeanPostProcessor后置处理→使用中→@PreDestroy→DisposableBean→自定义destroy方法。

在这里插入图片描述

什么是循环依赖?Spring如何解决?

  • 难点:构造器注入无法解决循环依赖
  • :多个Bean相互引用形成环。Spring通过三级缓存解决(singletonFactories、earlySingletonObjects、singletonObjects),仅支持单例Setter循环依赖:
// 三级缓存结构
singletonFactories  // 三级:存放Bean工厂(ObjectFactory)
earlySingletonObjects // 二级:存放早期引用
singletonObjects    // 一级:存放完整Bean

三级缓存解决循环依赖流程(Spring 6 优化)

关键点:三级缓存(singletonFactories)存储 ObjectFactory`,支持 AOP 代理对象的提前创建

构造器循环依赖能否解决?为什么?

:不能。因为构造器注入必须在实例化阶段完成,此时Bean未放入缓存。

@Autowired和@Resource的区别?

  • @Autowired按类型注入,支持@Qualifier指定名称。
  • @Resource(JDK标准注解)默认按名称注入,名称找不到则按类型。

如何注入集合类型(List/Map)?

:XML中通过<list><map>标签,或使用@Autowired注入所有匹配类型的Bean。

@Component, @Service, @Controller, @Repository的区别?

:功能相同,均为声明Bean。语义区分:

@Controller:Web层
@Service:业务层
@Repository:数据层(转换持久层异常)
@Component:通用组件

什么是延迟初始化(Lazy Initialization)?

:Bean在首次使用时才创建,而非容器启动时。通过@Lazy(true)配置。

如何条件化创建Bean?

:使用@Conditional注解,实现Condition接口自定义条件。

public class MyCondition implements Condition {public boolean matches(ConditionContext ctx, AnnotatedTypeMetadata meta) {return ctx.getEnvironment().containsProperty("enable.feature");}
}

FactoryBean和BeanFactory的区别?

  • BeanFactory是IoC容器根接口。
  • FactoryBean是创建复杂对象的工厂Bean,通过getObject()返回实际对象。

如何获取ApplicationContext?

:实现ApplicationContextAware接口或直接注入ApplicationContext

BeanDefinition的作用?

:定义Bean的元数据(类名、作用域、属性等),Spring根据BeanDefinition创建Bean。

PropertySourcesPlaceholderConfigurer的作用?

:解析${}占位符,替换为属性文件中的值。

Spring如何管理父子容器?

:通过HierarchicalBeanFactory接口,子容器可以访问父容器的Bean,但父容器不能访问子容器。


二、AOP(12题)

AOP的核心概念(Aspect, Joinpoint, Pointcut, Advice)?

  • 切面(Aspect):模块化的横切关注点(如日志)。
  • 连接点(Joinpoint):程序执行点(方法调用、异常抛出)。
  • 切点(Pointcut):匹配连接点的表达式。
  • 通知(Advice):在连接点执行的动作(Before/After等)。

Spring AOP和AspectJ的区别?

特性Spring AOPAspectJ
概念Spring AOP是Spring框架中的AOP实现,依赖于Spring的容器和其他基础设施AspectJ是一个独立的AOP框架,不依赖于任何框架或容器
实现动态代理(JDK/CGLIB)编译时/加载时织入
性能运行时开销无运行时开销
功能仅支持方法级别支持字段、构造器等

JDK动态代理和CGLIB代理的区别?

: Spring6仍优先使用 JDK 动态代理(需接口),但 CGLIB 移除了对 ASM 的依赖,改用 ByteBuddy

  • JDK代理:基于接口,使用ProxyInvocationHandler
  • CGLIB:基于继承,生成目标类的子类。

如何强制使用CGLIB代理?

:XML配置<aop:aspectj-autoproxy proxy-target-class="true"/>,或注解@EnableAspectJAutoProxy(proxyTargetClass=true)

五种通知类型及其执行时机?

  • @Before:方法执行前
  • @After:方法执行后(无论成功或异常)
  • @AfterReturning:方法正常返回后
  • @AfterThrowing:方法抛出异常后
  • @Around:环绕方法执行(可控制是否执行目标方法)
异常
正常
Around前
Before
目标方法
AfterThrowing
AfterReturning
After
Around后

如何定义切点(Pointcut)表达式?

:使用AspectJ表达式,如execution(* com.service.*.*(..))

如何在同一切面中控制通知的执行顺序?

:实现Ordered接口或使用@Order注解,值越小优先级越高。

如何获取目标方法的参数?

:在通知方法中使用JoinPoint参数(非环绕)或ProceedingJoinPoint(环绕),调用getArgs()

如何修改目标方法的返回值?

:在@AfterReturning中通过returning属性绑定返回值并修改,或在@Around中修改proceed()的返回值。

如何捕获目标方法抛出的异常?

:在@AfterThrowing中通过throwing属性绑定异常,或在@Around中捕获proceed()的异常。

AOP失效的常见场景

  • 目标类未由Spring管理
  • 方法非public
  • 自调用(this.method())
  • 未通过代理对象调用方法

@AspectJ与Schema-based AOP对比

特性@AspectJ风格XML Schema风格
可读性✅ (注解直观)❌ (配置冗长)
编译依赖❌ (纯运行时)
切点表达式✅ (强大灵活)⚠️ (功能受限)

三、事务管理(9题)

Spring事务管理的两种方式?

  • 编程式:TransactionTemplate.execute()
  • 声明式:@Transactional注解

声明式事务实现原理

答:基于AOP + TransactionInterceptor拦截器

@Transactional注解可以作用在哪些位置?

:类(所有public方法)、接口(不推荐)、方法(推荐public方法)。

事务传播行为(Propagation)有哪些?

@Transactional(propagation = Propagation.REQUIRES_NEW)
public void methodA() {// 新建独立事务,外层事务挂起
}

  • REQUIRED(默认):当前有事务则加入,否则新建。
  • REQUIRES_NEW:新建事务,挂起当前事务。
  • SUPPORTS:有事务则加入,否则非事务运行。
  • NOT_SUPPORTED:非事务运行,挂起当前事务。
  • MANDATORY:必须存在事务,否则抛异常。
  • NEVER:必须不存在事务,否则抛异常。
  • NESTED:嵌套事务(使用保存点)。

事务隔离级别(Isolation)有哪些?

  • DEFAULT:使用数据库默认。
  • READ_UNCOMMITTED:读未提交。
  • READ_COMMITTED:读已提交。
  • REPEATABLE_READ:可重复读。
  • SERIALIZABLE:串行化。
隔离级别脏读不可重复读幻读
READ_UNCOMMITTED
READ_COMMITTED
REPEATABLE_READ
SERIALIZABLE

@Transactional失效的常见场景?

  • 方法非public。
  • 自调用(未经过代理)。
  • 异常被捕获未抛出。
  • 异常类型非RuntimeException且未指定rollbackFor
  • 数据库引擎不支持事务(如MyISAM)。
// 自调用导致事务失效
public void save() {this.update(); // 未经过代理类
}
@Transactional
public void update() {...}
调用方式是否经过代理事务是否生效
外部调用update()
this.update()
  • 解决:注入自身代理对象 @Autowired private MyService self;

如何指定回滚的异常类型?

:使用@Transactional(rollbackFor = MyException.class)

事务超时如何设置?

@Transactional(timeout = 5)(单位:秒)。

只读事务的作用?

@Transactional(readOnly=true),优化数据库引擎(如MySQL只读时使用InnoDB只读视图)。


四、Spring MVC(12题)

Spring MVC核心组件及其作用?

  • DispatcherServlet:前端控制器,统一调度。
  • HandlerMapping:映射请求到处理器。
  • HandlerAdapter:执行处理器。
  • ViewResolver:解析视图名到具体视图。
  • HandlerExceptionResolver:处理异常。

DispatcherServlet请求处理流程?

在这里插入图片描述

@RequestMapping和@GetMapping的区别?

  • @GetMapping@RequestMapping(method=RequestMethod.GET)的简写。
  • @RequestMapping底层:RequestMappingHandlerMapping解析注解注册路由

如何获取请求参数?参数绑定原理

: 关键类:HandlerMethodArgumentResolver实现类

  • @RequestParam:获取查询参数。
  • @PathVariable:获取路径参数。
  • @RequestBody:获取请求体(JSON/XML)。
  • HttpServletRequest:原生对象。

如何返回JSON数据?

:使用@RestController@ResponseBody,并添加Jackson依赖。

如何实现文件上传?

@PostMapping("/upload")
public String upload(@RequestParam("file") MultipartFile file) {file.transferTo(new File("/path/to/save"));
}

如何统一处理异常?

:使用@ControllerAdvice + @ExceptionHandler

@ControllerAdvice
public class GlobalExceptionHandler {@ExceptionHandler(Exception.class)public ModelAndView handleException(Exception ex) {ModelAndView mav = new ModelAndView("error");mav.addObject("msg", ex.getMessage());return mav;}
}

拦截器(Interceptor) vs 过滤器(Filter)?

  • 过滤器:Servlet规范,处理所有请求。
  • 拦截器:Spring MVC机制,可访问Handler上下文。

如何自定义拦截器?

:实现HandlerInterceptor接口,重写preHandlepostHandleafterCompletion方法,并注册到WebMvcConfigurer

如何实现重定向?

:返回"redirect:/path"

如何获取请求头信息?

:使用@RequestHeader("User-Agent") String userAgent

如何实现数据验证?

:使用@Valid注解参数,并配合JSR-303注解(如@NotNull)在实体类上。


五、Spring Data JPA(8题)

JPA和Hibernate的关系?

:JPA是规范,Hibernate是实现。

Spring Data JPA的核心接口?

CrudRepository(基础CRUD)、JpaRepository(扩展分页/排序)。

如何定义查询方法?

:根据方法名自动解析,如findByUsername(String username)

@Query注解的作用?

:自定义JPQL或原生SQL查询。

如何实现分页查询?

Page<User> findAll(Pageable pageable);
// 调用:repository.findAll(PageRequest.of(0, 10));

实体类状态有哪些?

:瞬时(Transient)、托管(Managed)、游离(Detached)、删除(Removed)。

在这里插入图片描述

如何解决N+1查询问题?

  • 使用@EntityGraph指定关联加载策略。
  • JPQL中写JOIN FETCH
@EntityGraph(attributePaths = "orders") // 指定立即加载的关联
List<User> findAll();

乐观锁实现

@Entity
public class Product {@Versionprivate Long version; // 更新时自动校验版本
}

六、高频难点(10题)

FactoryBean与普通Bean的区别

:FactoryBean返回的是getObject()方法创建的对象,而非自身;FactoryBean 优先初始化,但其生产的 Bean 延迟加载(需通过 & 前缀获取 FactoryBean 本身)

BeanPostProcessor扩展点实战

public class CustomBeanPostProcessor implements BeanPostProcessor {public Object postProcessBeforeInitialization(Object bean, String name) {if (bean instanceof MyService) {// 修改Bean实例}return bean;}
}

如何动态注册Bean?

:通过BeanDefinitionRegistry编程式注册:

GenericBeanDefinition beanDef = new GenericBeanDefinition();
beanDef.setBeanClass(MyBean.class);
registry.registerBeanDefinition("myBean", beanDef);

Spring事件机制模型

  • 三要素
    ApplicationEvent(事件)→ ApplicationListener(监听器)→ ApplicationEventPublisher(发布器)

@Transactional事务传播嵌套场景分析

@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {methodB(); // 内嵌事务
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void methodB() {...}

AOT 编译对 Bean 生命周期的限制?

:AOT 模式下不支持动态代理,需提前生成代理类;BeanPostProcessor 需静态注册。

响应式编程在 Spring 6 的整合

ReactiveAdapterRegistry 支持 Project Reactor/RxJava,与 Spring WebFlux 深度集成。

如何实现 GraalVM 原生镜像支持?

  • 使用 spring-boot-aot-plugin 生成反射配置
  • 排除动态代理类
  • 通过 @NativeHint 定义原生元数据。

Spring 6 中的 JdbcClient 新特性

:简化 JDBC 操作,链式调用代替 JdbcTemplate

jdbcClient.sql("SELECT * FROM user WHERE id=?")
.param(1)
.query(User.class);

ProblemDetail 与国际化(i18n)的整合

:通过 MessageSource 动态填充错误消息

detail.setDetail(messageSource.getMessage("error.not_found", null, locale));
http://www.dtcms.com/a/265494.html

相关文章:

  • 朝鲜APT组织使用Nim语言恶意软件对macOS发起隐秘Web3与加密货币攻击
  • 中国户外品牌全球竞争力榜单发布:科技突围与文化赋能重塑行业格局
  • 现代工程科技杂志投稿
  • 后端MVC(控制器与动作方法的关系)
  • 微服务外联Feign调用:第三方API调用的负载均衡与容灾实战
  • C++之路:类基础、构造析构、拷贝构造函数
  • Rust Web 全栈开发(一):构建 TCP Server
  • Go基础(Gin)
  • Webpack 5 核心机制详解与打包性能优化实践
  • 牛客:HJ16 购物单【01背包】【华为机考】
  • 前端单元测试覆盖率工具有哪些,分别有什么优缺点
  • 在 Sepolia 上使用 Zama fhEVM 构建隐私代币与流动性池:全流程实战指南
  • Android音视频探索之旅 | CMake基础语法 创建支持Ffmpeg的Android项目
  • 【免费.NET方案】CSV到PDF与DataTable的快速转换
  • 音频动态压缩算法曲线实现
  • C++【成员变量、成员函数、this指针】
  • OSPF高级特性之FRR
  • Vue 项目在哪里加载「字典数据」最好
  • 基于 alpine 构建 .net 的基础镜像
  • 开源模型应用落地-让AI更懂你的每一次交互-Mem0集成Qdrant、Neo4j与Streamlit的创新实践(四)
  • Zookeeper 客户端 .net访问框架 ZookeeperNetEx项目开发编译
  • 开源 C# .net mvc 开发(六)特殊控制控制台、周期、邮件编程
  • 深度实战:Ubuntu服务器宕机排查全记录
  • 月付物理服务器租用平台-青蛙云
  • 基于 govaluate 的监控系统中,如何设计灵活可扩展的自定义表达式函数体系
  • npm ERR! code ERESOLVE npm ERR! ERESOLVE unable to resolve dependency tree
  • Python Set() 完全指南:从入门到精通
  • R语言开发记录,一
  • 前端-HTML-day1
  • Rust Web 全栈开发(二):构建 HTTP Server