【八股必备】框架篇面试题2
一、Spring 基础与核心思想面试官请你先简单介绍一下你对 Spring 框架的理解它主要解决了什么问题候选人Spring 是一个轻量级的 Java 企业级应用开发框架其核心是IoC控制反转和AOP面向切面编程。IoC将对象的创建和依赖关系的管理从代码中剥离出来交给 Spring 容器统一管理从而降低组件之间的耦合度提高代码的可测试性和可维护性。AOP允许我们将日志、事务、安全等横切关注点从业务逻辑中分离出来实现模块化复用避免了代码的侵入和重复。除了这两大核心Spring 还提供了一整套企业级解决方案包括数据访问JDBC、ORM 集成、事务管理、MVC Web 框架、以及与其他框架如 MyBatis、Hibernate的无缝集成。它通过分层架构和模块化设计让开发者可以按需引入功能避免了臃肿的全量引入。近年来Spring 生态更是蓬勃发展Spring Boot通过“约定大于配置”的理念大幅简化了项目搭建和配置Spring Cloud则为微服务架构提供了服务发现、配置中心、断路器等一系列开箱即用的组件。可以说Spring 已经成为 Java 后端开发的事实标准。二、IoC 与依赖注入面试官你能详细说一下 IoC控制反转和 DI依赖注入的关系吗Spring 中依赖注入有哪些方式候选人IoC 是一种设计思想它将传统上由程序代码直接控制的对象创建和依赖关系的控制权反转给容器。而DI依赖注入是实现 IoC 的一种具体方式即由容器在运行时动态地将依赖对象注入到组件中。Spring 支持三种主要的依赖注入方式构造器注入通过构造函数传递依赖保证依赖不可变且对象初始化后即可使用推荐用于强制依赖。javaService public class UserService { private final UserRepository userRepository; public UserService(UserRepository userRepository) { this.userRepository userRepository; } }Setter 方法注入通过 Setter 方法设置依赖灵活性高适合可选依赖但可能导致对象状态不完整。javaService public class PaymentService { private PaymentGateway gateway; Autowired public void setGateway(PaymentGateway gateway) { this.gateway gateway; } }字段注入直接使用Autowired注解在字段上代码最简洁但隐藏了依赖关系不利于单元测试和不可变对象设计通常不推荐在生产代码中使用。从 Spring 4.3 开始如果类只有一个构造器可以省略Autowired容器会自动选择该构造器进行注入。面试官如果让你自己设计一个 IoC 容器你会从哪些方面考虑候选人我会从以下几个核心方面入手Bean 的生命周期管理需要定义 Bean 的创建、初始化、销毁等阶段可以使用工厂模式和单例模式来管理实例。依赖注入机制需要支持构造器注入、Setter 注入、字段注入可以借助 Java 反射在运行时动态注入依赖。Bean 的作用域支持单例、原型、请求、会话等作用域可以用不同的 Map 来存储对应作用域的 Bean。AOP 支持在容器中集成动态代理允许用户通过切面编程增强 Bean 的功能。配置加载支持 XML、注解、Java Config 等多种配置方式将配置信息解析为 BeanDefinition 并注册到容器。异常处理对 Bean 创建失败、依赖缺失等异常提供清晰的错误信息和回滚机制。扩展点提供类似 BeanPostProcessor、BeanFactoryPostProcessor 的扩展接口允许用户在 Bean 初始化前后或容器刷新前后执行自定义逻辑。三、AOP 与动态代理面试官Spring AOP 的实现原理是什么它和 AspectJ 有什么区别候选人Spring AOP 是基于动态代理实现的。它支持两种代理方式JDK 动态代理要求目标对象必须实现至少一个接口通过java.lang.reflect.Proxy和InvocationHandler在运行时生成代理对象。CGLIB 动态代理当目标类没有实现接口时Spring 会使用 CGLIB 库生成目标类的子类作为代理通过继承方式实现方法拦截。Spring AOP 属于运行时增强而 AspectJ 是编译时增强也可以通过加载时织入。AspectJ 提供了更强大、更细粒度的切面功能如构造器切点、字段访问切点但需要额外的编译或类加载处理。Spring 通常将 AspectJ 作为注解风格的补充但底层实现仍采用 Spring 自身的动态代理机制。面试官你能介绍一下 AOP 中的核心概念吗比如 Aspect、Join Point、Advice、Pointcut 分别是什么候选人Aspect切面是通知和切点的结合定义了什么功能通知在什么地方切点执行。通常使用Aspect注解的类表示。Join Point连接点程序执行过程中的一个点比如方法调用、异常抛出等。在 Spring AOP 中连接点始终是方法执行。Advice通知切面在特定连接点执行的动作包括Before、After、Around、AfterReturning、AfterThrowing等类型。Pointcut切点匹配连接点的表达式用于定位通知应该织入哪些连接点。例如execution(* com.example.service.*.*(..))。Introduction引介允许切面为目标对象引入新的接口和实现。Weaving织入将切面应用到目标对象并创建代理对象的过程。Spring AOP 在运行时通过动态代理织入。AOP Proxy代理由 AOP 框架生成的代理对象用于执行通知逻辑和目标方法。Target Object目标对象被代理的原始对象。面试官静态代理为什么不适合实现 AOP能举个例子说明吗候选人静态代理虽然可以实现方法增强但在生产环境中几乎不可用主要有三大痛点代码爆炸如果需要对 100 个 Service 类添加事务就需要手写 100 个代理类每个类里都是重复的 try-catch 提交回滚代码维护成本极高。僵化一旦业务接口发生变化比如方法名改动所有相关代理类都需要同步修改而动态代理通过反射调用目标方法完全不受这种变动影响。无法动态筛选静态代理只能写死要增强的方法而 Spring AOP 可以通过切点表达式灵活匹配需要增强的方法例如只对带Transactional注解的方法增强。因此Spring 选择了运行时动态代理一个切面配置就能覆盖成百上千个方法极大提高了开发效率和可维护性。四、Spring 中的设计模式面试官Spring 框架中都用到了哪些设计模式你能举例说明吗候选人Spring 中大量使用了经典设计模式常见的有工厂模式BeanFactory和ApplicationContext是工厂模式的具体体现负责创建和管理 Bean 实例。单例模式Spring Bean 默认作用域是单例通过单例注册表如singletonObjectsMap保证每个 Bean 只有一个实例。代理模式Spring AOP 通过 JDK 或 CGLIB 动态代理实现切面增强。模板方法模式JdbcTemplate、RestTemplate、TransactionTemplate等模板类封装了固定流程将可变部分交给回调接口处理。观察者模式Spring 事件机制ApplicationEvent和ApplicationListener就是观察者模式的典型应用。适配器模式Spring MVC 中的HandlerAdapter统一了不同 Controller 的调用方式AOP 中的AdvisorAdapter将不同类型的通知适配为MethodInterceptor。策略模式Spring 资源访问接口Resource及其实现类ClassPathResource、FileSystemResource体现了策略模式AOP 代理创建策略也使用了策略模式JDK 或 CGLIB。装饰器模式BeanWrapper对 Bean 实例进行包装添加属性操作功能TransactionAwareCacheDecorator为缓存增加事务支持。责任链模式Spring MVC 的拦截器链就是责任链模式的体现。原型模式原型作用域的 Bean 每次获取都会创建新实例通过克隆或反射实现。五、Bean 的生命周期与作用域面试官你能详细描述一下 Spring Bean 的生命周期吗候选人Spring Bean 的生命周期可以概括为以下几个阶段实例化容器通过反射或工厂方法创建 Bean 实例。属性赋值根据配置XML、注解对 Bean 的属性进行依赖注入。Aware 接口回调如果 Bean 实现了BeanNameAware、BeanFactoryAware、ApplicationContextAware等接口则回调相应方法传入容器信息。BeanPostProcessor 前置处理调用所有注册的BeanPostProcessor的postProcessBeforeInitialization方法。初始化如果 Bean 实现了InitializingBean接口调用afterPropertiesSet()。如果配置了init-method或PostConstruct则调用指定的初始化方法。BeanPostProcessor 后置处理调用postProcessAfterInitialization方法此时 Bean 已准备就绪可以返回给调用者。使用中Bean 驻留在容器中供应用程序使用。销毁如果 Bean 实现了DisposableBean接口调用destroy()。如果配置了destroy-method或PreDestroy则调用指定的销毁方法。面试官Spring Bean 默认是单例的那么单例 Bean 和原型 Bean 的生命周期有何不同候选人创建时机单例 Bean 默认在容器启动时创建也可配置为懒加载而原型 Bean 每次请求时都会创建新实例。初始化流程两者都会完整执行属性注入、Aware 回调、BeanPostProcessor 前后置处理、初始化方法等生命周期步骤。销毁管理单例 Bean 由容器管理销毁在容器关闭时触发PreDestroy或DisposableBean而原型 Bean 在创建后容器便不再跟踪需要使用者自行释放资源Spring 不会调用其销毁方法。内存占用单例常驻内存适合无状态服务原型每次创建新实例适合有状态对象但需注意内存开销和资源释放。面试官Spring Bean 的作用域除了 singleton 和 prototype还有哪些候选人在 Web 环境下Spring 还提供了request每个 HTTP 请求创建一个新 Bean仅在当前请求内有效。session每个 HTTP Session 创建一个 Bean在同一个会话内共享。application整个 ServletContext 生命周期内只有一个 Bean。websocket每个 WebSocket 会话内共享一个 Bean。 此外开发者还可以通过实现Scope接口自定义作用域。六、循环依赖与三级缓存面试官Spring 是如何解决循环依赖问题的为什么要用三级缓存二级缓存不行吗候选人Spring 通过三级缓存来解决单例作用域下 Setter/字段注入的循环依赖问题。三级缓存定义在DefaultSingletonBeanRegistry中一级缓存singletonObjects存储完全初始化好的单例 Bean。二级缓存earlySingletonObjects存储早期暴露的 Bean 引用半成品已完成实例化但未完成属性注入和初始化。三级缓存singletonFactories存储ObjectFactory对象用于生成早期引用。假设存在 A 依赖 BB 依赖 A 的循环依赖处理流程如下创建 A 时实例化后立即将 A 的ObjectFactory放入三级缓存。填充 A 的属性时发现需要 B于是去创建 B。创建 B 时同样实例化并将 B 的工厂放入三级缓存。填充 B 的属性时发现需要 A此时从三级缓存获取 A 的工厂调用getObject()得到 A 的早期引用可能是原始对象或提前生成的代理存入二级缓存并删除三级缓存中的 A 工厂。B 将得到的 A 引用注入完成初始化后存入一级缓存。回到 A将已初始化的 B 注入完成 A 的初始化并存入一级缓存。为什么必须三级缓存核心在于AOP 代理。如果 A 需要被代理那么 B 注入的 A 必须是代理对象而非原始对象否则会造成单例不一致。三级缓存中的ObjectFactory可以在调用getObject()时智能判断如果 A 需要代理则提前生成代理对象否则返回原始对象。如果只用二级缓存就只能缓存一个固定的对象无法动态决定是否生成代理导致代理对象和原始对象混淆破坏单例约束。面试官三级缓存的数据结构是怎样的候选人三级缓存都是Map类型singletonObjectsConcurrentHashMapString, ObjectearlySingletonObjectsHashMapString, Object非并发因为访问时已有锁singletonFactoriesHashMapString, ObjectFactory?七、事务管理面试官Spring 的事务管理是如何实现的Transactional 注解在什么情况下会失效候选人Spring 的事务管理基于 AOP 实现通过代理对象拦截带Transactional注解的方法在方法执行前开启事务执行后根据是否抛出异常决定提交或回滚。Transactional失效的常见场景包括未捕获异常如果方法内抛出了异常但被 catch 住事务无法感知不会回滚除非手动设置回滚。非受检异常默认回滚受检异常默认不回滚Spring 默认只对RuntimeException和Error回滚若想让受检异常也回滚需设置rollbackFor。事务传播属性设置不当例如在同一个类中方法 A无事务调用方法 B有事务若通过this直接调用则不会经过代理事务失效。方法不是 publicTransactional标注在非 public 方法上不会生效代理机制限制。多数据源事务未正确配置未指定事务管理器或使用了不支持分布式事务的数据源。数据库引擎不支持事务如 MyISAM 引擎。面试官如果一个事务方法内部调用另一个方法且使用 this 调用事务会生效吗候选人不会生效。因为 Spring 事务是通过代理对象实现的只有通过代理对象调用方法才会触发事务拦截器。直接使用this调用相当于绕过了代理被调用的方法上的Transactional将不起作用。 解决方案通过AopContext.currentProxy()获取当前代理对象。将方法拆分到另一个 Service 类中通过注入的代理对象调用。使用编程式事务。八、Spring MVC面试官你能描述一下 Spring MVC 的处理流程吗从用户发送请求到返回响应的完整过程。候选人Spring MVC 的核心组件是DispatcherServlet处理流程如下用户发送请求到前端控制器DispatcherServlet。DispatcherServlet调用HandlerMapping根据请求 URL 查找对应的 HandlerController和拦截器链。返回 HandlerExecutionChain 给DispatcherServlet。DispatcherServlet根据 Handler 类型选择合适的HandlerAdapter。HandlerAdapter执行 HandlerController方法处理业务逻辑返回ModelAndView。HandlerAdapter将ModelAndView返回给DispatcherServlet。DispatcherServlet将ModelAndView交给ViewResolver进行视图解析得到具体的View。View渲染视图将 Model 数据填充到视图。响应结果返回给用户。面试官HandlerMapping 和 HandlerAdapter 的作用是什么候选人HandlerMapping负责根据请求找到对应的处理器Controller。常见的实现有RequestMappingHandlerMapping基于注解、BeanNameUrlHandlerMapping基于 Bean 名称等。HandlerAdapter负责调用处理器的方法。不同的处理器类型如实现了Controller接口、HttpRequestHandler接口或使用RequestMapping注解的方法需要不同的适配器进行调用HandlerAdapter屏蔽了这些差异使得DispatcherServlet可以统一处理。九、Spring Boot 自动配置与启动原理面试官Spring Boot 的自动配置是如何实现的你能简单说一下原理吗候选人Spring Boot 的自动配置基于EnableAutoConfiguration注解和spring.factories文件实现。SpringBootApplication组合了EnableAutoConfiguration后者通过Import(AutoConfigurationImportSelector.class)导入配置类。AutoConfigurationImportSelector会扫描所有依赖 jar 包中的META-INF/spring.factories文件读取其中org.springframework.boot.autoconfigure.EnableAutoConfiguration对应的配置类列表。对于每一个候选配置类Spring Boot 会根据一系列条件注解如ConditionalOnClass、ConditionalOnMissingBean、ConditionalOnProperty等判断是否满足加载条件只有满足条件时才会将该配置类中的 Bean 注册到容器中。最终实现根据项目依赖自动装配所需的功能如数据源、Web 容器、消息队列等。面试官如何理解 Spring Boot 的“约定大于配置”原则候选人“约定大于配置”是 Spring Boot 的核心设计理念通过预设合理的默认值和项目结构大幅减少开发者的配置工作。主要体现在自动化配置引入spring-boot-starter-web后自动配置内嵌 Tomcat 和 Spring MVC无需手动编写 web.xml 或配置 DispatcherServlet。默认配置如默认日志级别、默认静态资源位置/static、/public、默认配置文件application.properties/yml等。约定的项目结构主程序类放在根包下控制器放在controller包服务放在service包便于组件扫描自动发现。 这种约定使开发者可以快速启动项目同时允许在需要时覆盖默认配置兼顾了灵活性和开发效率。面试官Spring Boot 的启动流程是怎样的候选人Spring Boot 的启动入口是SpringApplication.run(主类.class, args)核心流程如下创建SpringApplication实例进行初始化包括设置应用类型、读取spring.factories中的初始化器和监听器。调用run方法启动StopWatch记录启动时间。创建并配置ApplicationContext根据 web 环境选择AnnotationConfigServletWebServerApplicationContext等。准备环境Environment包括加载配置文件、命令行参数等并通知监听器。执行ApplicationContext的刷新refresh——这是 Spring 容器的核心步骤包括 BeanFactory 的初始化、Bean 的实例化、自动配置的加载等。刷新完成后调用ApplicationRunner或CommandLineRunner执行自定义启动逻辑。返回ConfigurableApplicationContext启动完成。十、Spring Boot Starter 与扩展点面试官你有没有自己写过 Spring Boot Starter如果有能说说步骤吗候选人我写过简单的自定义 Starter主要步骤如下创建 Maven 项目引入spring-boot-autoconfigure和spring-boot-starter依赖。定义配置属性类使用ConfigurationProperties绑定配置前缀例如my.service.name。创建自动配置类使用Configuration和EnableConfigurationProperties启用属性类并声明需要自动装配的 Bean如 Service 实现。添加条件注解如ConditionalOnMissingBean、ConditionalOnProperty确保在用户未自定义时生效。在META-INF/spring.factories中注册自动配置类textorg.springframework.boot.autoconfigure.EnableAutoConfigurationcom.example.MyAutoConfiguration打包发布可发布到本地 Maven 仓库或私服。 使用时其他项目只需引入该 Starter 依赖并在配置文件中指定相关属性即可。面试官Spring Boot 中常用的扩展点有哪些候选人Spring Boot/Spring 提供了丰富的扩展点ApplicationRunner / CommandLineRunner容器启动后执行特定代码。BeanPostProcessor在 Bean 初始化前后进行自定义处理。BeanFactoryPostProcessor在 Bean 实例化前修改 Bean 定义。ImportSelector / ImportBeanDefinitionRegistrar动态注册 Bean 定义。EnvironmentPostProcessor在环境准备阶段自定义属性源。ApplicationContextInitializer在刷新容器前对上下文进行初始化。SpringApplicationRunListener监听 Spring Boot 启动过程的不同阶段。HandlerInterceptorMVC拦截 Controller 请求。ControllerAdvice全局异常处理、数据绑定等。十一、Spring Cloud 微服务面试官你了解 Spring Cloud 吗它和 Spring Boot 有什么关系候选人Spring Cloud 是基于 Spring Boot 构建的微服务工具集它为分布式系统开发提供了服务发现与注册、配置中心、负载均衡、断路器、API 网关、分布式追踪等常见模式的一站式解决方案。 Spring Boot 用于快速构建单个微服务应用而 Spring Cloud 则将这些微服务连接起来协调它们之间的通信与管理。两者相辅相成Spring Boot 提供独立运行的微服务单元Spring Cloud 提供这些单元在分布式环境下的协作能力。面试官你用过哪些 Spring Cloud 组件能介绍一下它们的作用吗候选人我主要接触过 Spring Cloud Netflix 和 Spring Cloud Alibaba 生态中的组件服务注册与发现Eureka已停更、Nacos、Consul。微服务启动时向注册中心注册自身信息消费者通过注册中心获取服务列表实现服务间的动态调用。负载均衡Ribbon已进入维护、Spring Cloud LoadBalancer。在客户端实现请求的负载均衡策略如轮询、随机、权重等。声明式 HTTP 客户端Feign / OpenFeign。通过接口加注解的方式定义 HTTP 请求整合了 Ribbon 和 Hystrix简化服务调用。断路器Hystrix已停更、Sentinel、Resilience4J。当依赖的服务故障时快速失败并降级防止雪崩效应。配置中心Spring Cloud Config、Nacos Config。集中管理所有微服务的配置文件支持动态刷新。API 网关Zuul已停更、Spring Cloud Gateway。提供路由、过滤、限流、安全等统一入口功能。分布式链路追踪Sleuth Zipkin。跟踪请求在多个服务间的调用链帮助分析性能瓶颈和故障点。消息总线Spring Cloud Bus。用于传播配置变更等事件到所有服务实例。面试官负载均衡有哪些常见的算法如何保证同一个用户的请求总是落到同一台服务器候选人常见负载均衡算法包括轮询按顺序分发请求。加权轮询根据服务器性能设置权重权重高的处理更多请求。随机随机选择一台服务器。加权随机带权重的随机选择。最少连接数将请求分配给当前活跃连接数最少的服务器。一致性哈希根据请求的某个特征如用户 ID、IP进行哈希将请求映射到特定服务器保证相同特征的请求始终落到同一台服务器常用于有状态会话保持。要实现同一个用户始终打到同一台服务器可以使用一致性哈希算法。例如在 Nginx 或 Spring Cloud LoadBalancer 中可以配置基于用户 IP 或用户 ID 的哈希策略确保相同来源的请求被路由到同一后端实例避免 Session 丢失问题。面试官服务熔断和服务降级有什么区别候选人服务熔断是一种链路保护机制类似于电路保险丝。当某个服务异常如超时、失败率过高达到阈值时熔断器打开后续请求直接返回错误或走降级逻辑不再调用实际服务避免故障蔓延。一段时间后熔断器尝试半开状态放行部分请求若成功则关闭熔断器。服务降级是一种有损服务的策略通常在服务器压力过大或某些非核心服务不可用时暂时关闭或简化这些服务以释放资源保障核心业务。降级可以是主动的如双十一期间关闭评论功能或被动的熔断后触发的 fallback 逻辑。 简单来说熔断是应对服务故障的自动反应降级是整体系统资源不足时的主动取舍。在 Spring Cloud 中Hystrix 或 Sentinel 可以同时实现熔断和降级。十二、补充高频面试题面试官Autowired 和 Resource 有什么区别候选人来源不同Autowired是 Spring 定义的注解Resource是 JSR-250 规范定义的注解属于 Java 原生注解。装配方式Autowired默认按类型byType装配如果需要按名称装配需结合Qualifier。Resource默认按名称byName装配如果找不到名称匹配的 Bean则按类型装配。适用场景在 Spring 项目中两者均可使用但官方推荐使用Autowired因为它与 Spring 集成更紧密且支持构造器注入。如果希望与 Spring 解耦可以使用Resource。面试官Spring 中如何保证线程安全候选人Spring 默认的 Bean 是单例的因此在多线程环境下需要关注线程安全无状态 Bean如 Service、DAO本身就是线程安全的因为它们不持有可变的成员变量。有状态 Bean可以改为原型作用域每次请求都创建新实例。如果必须使用单例且包含可变状态可以通过同步机制如synchronized、Lock或使用线程安全的数据结构如ConcurrentHashMap来保证线程安全。另外可以利用ThreadLocal将可变变量与线程绑定避免共享。面试官Spring 中的事件机制是如何工作的候选人Spring 事件机制基于观察者模式包含三个角色事件ApplicationEvent继承自ApplicationEvent的类封装事件数据。监听器ApplicationListener实现ApplicationListener接口或使用EventListener注解的方法处理特定事件。事件发布器ApplicationEventPublisher通常通过注入ApplicationEventPublisher来发布事件。 流程当发布者调用publishEvent时Spring 容器会通知所有对该事件感兴趣的监听器并同步或异步执行监听逻辑可通过Async实现异步。事件机制常用于模块间解耦例如用户注册成功后发送邮件、积分增加等操作。面试官Spring Boot 中如何实现异步调用候选人Spring Boot 中可通过Async注解轻松实现异步方法调用在启动类或配置类上添加EnableAsync开启异步支持。在需要异步执行的方法上添加Async注解。默认使用SimpleAsyncTaskExecutor建议自定义线程池例如javaBean public Executor taskExecutor() { ThreadPoolTaskExecutor executor new ThreadPoolTaskExecutor(); executor.setCorePoolSize(5); executor.setMaxPoolSize(10); executor.setQueueCapacity(100); executor.initialize(); return executor; }注意异步方法必须通过代理对象调用即不同类中调用或注入自身否则Async不会生效。