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。

特性 BeanFactory ApplicationContext
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循环依赖:
java 复制代码
// 三级缓存结构
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接口自定义条件。

java 复制代码
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 AOP AspectJ
概念 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)有哪些?

java 复制代码
@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)。
java 复制代码
// 自调用导致事务失效
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依赖。

如何实现文件上传?

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

如何统一处理异常?

:使用@ControllerAdvice + @ExceptionHandler

java 复制代码
@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查询。

如何实现分页查询?

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

实体类状态有哪些?

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

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

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

乐观锁实现

java 复制代码
@Entity
public class Product {
    @Version
    private Long version; // 更新时自动校验版本
}

六、高频难点(10题)

FactoryBean与普通Bean的区别

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

BeanPostProcessor扩展点实战

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

如何动态注册Bean?

:通过BeanDefinitionRegistry编程式注册:

java 复制代码
GenericBeanDefinition beanDef = new GenericBeanDefinition();
beanDef.setBeanClass(MyBean.class);
registry.registerBeanDefinition("myBean", beanDef);

Spring事件机制模型

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

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

java 复制代码
@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

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

ProblemDetail 与国际化(i18n)的整合

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

java 复制代码
detail.setDetail(messageSource.getMessage("error.not_found", null, locale));
相关推荐
DES 仿真实践家16 分钟前
【Day 11-N22】Python类(3)——Python的继承性、多继承、方法重写
开发语言·笔记·python
llwszx4 小时前
深入理解Java锁原理(一):偏向锁的设计原理与性能优化
java·spring··偏向锁
云泽野5 小时前
【Java|集合类】list遍历的6种方式
java·python·list
IMPYLH6 小时前
Python 的内置函数 reversed
笔记·python
麦兜*8 小时前
Spring Boot启动优化7板斧(延迟初始化、组件扫描精准打击、JVM参数调优):砍掉70%启动时间的魔鬼实践
java·jvm·spring boot·后端·spring·spring cloud·系统架构
小赖同学啊8 小时前
物联网数据安全区块链服务
开发语言·python·区块链
码荼9 小时前
学习开发之hashmap
java·python·学习·哈希算法·个人开发·小白学开发·不花钱不花时间crud
天涯学馆9 小时前
前端开发也能用 WebAssembly?这些场景超实用!
前端·javascript·面试
小陈phd10 小时前
李宏毅机器学习笔记——梯度下降法
人工智能·python·机器学习
kk爱闹10 小时前
【挑战14天学完python和pytorch】- day01
android·pytorch·python