【Spring全家桶必问篇】2025年精选高频面试题深度解析(附代码示例)

【Spring全家桶必问篇】2025年精选高频面试题深度解析(附代码示例)

金三银四,金九银十,又到了面试季。Spring作为Java开发领域事实上的标准,无疑是面试中绕不开的重中之重。本文系统梳理了Spring Framework、Spring MVC、Spring Boot及Spring Cloud中的核心高频面试题,并附上详细解析和代码示例,助你一举拿下心仪的Offer!

1. Spring Framework 核心概念

在这里插入图片描述

1.1 什么是IoC(控制反转)和DI(依赖注入)?它们有什么关系?

  • IoC (Inversion of Control)控制反转,是一种设计思想。它将传统上由程序代码直接操控的对象调用权交给容器(如Spring IoC容器)来统一管理。简单说,就是将对象的创建、依赖关系的组装控制权从应用程序代码中反转到外部容器

  • DI (Dependency Injection)依赖注入,是实现IoC思想的一种主要方式。容器通过反射机制,动态地将某个依赖对象注入到组件(如Bean)中。常见注入方式有:构造器注入、Setter方法注入、字段注入。

  • 关系DI是IoC的实现手段之一。我们通过DI这种具体技术来实现IoC的松散耦合设计目标。

代码示例(构造器注入):

// Service 层
public interface UserService {
    String getUserName();
}

@Service
public class UserServiceImpl implements UserService {
    @Override
    public String getUserName() {
        return "程序员";
    }
}

// Controller 层,通过构造器注入 UserService
@Controller
public class UserController {
    private final UserService userService;

    // Spring容器会自动查找UserService类型的Bean并注入此处
    public UserController(UserService userService) {
        this.userService = userService;
    }

    public void printUserName() {
        System.out.println(userService.getUserName());
    }
}

1.2 Spring中Bean的作用域(Scope)有哪些?

Spring为Bean定义了多种作用域,你可以通过@Scope注解来指定。

作用域描述适用场景
singleton (默认)IoC容器中仅存在一个Bean实例,所有对该Bean的引用都共享这一实例。无状态的Bean,如工具类、Service、DAO等。
prototype每次从容器中获取Bean时,都会创建一个新的实例有状态的Bean,需要保持各自状态的Bean。
request每一次HTTP请求都会创建一个新的Bean,仅在Web应用中有效。用于存储一次请求的上下文信息。
session同一个HTTP Session共享一个Bean,不同Session使用不同的Bean,仅在Web应用中有效。用于存储用户会话信息,如用户登录状态。
application一个ServletContext生命周期内只创建一个Bean。全局上下文信息。
websocket在一个WebSocket的生命周期内只创建一个Bean。WebSocket通信相关。

配置示例:

@Bean
@Scope("prototype") // 或者 @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public MyPrototypeBean myPrototypeBean() {
    return new MyPrototypeBean();
}

1.3 Spring Bean的生命周期是怎样的?

这是一个非常经典的问题。简单来说,一个Bean从创建到销毁经历了以下关键步骤:

  1. 实例化 (Instantiate): 容器通过反射调用构造方法创建Bean实例。
  2. 属性赋值 (Populate Properties): 容器注入Bean的依赖(其他Bean)和配置值(如@Value)。
  3. BeanPostProcessor前置处理: 调用所有BeanPostProcessorpostProcessBeforeInitialization方法。
  4. 初始化 (Initialization)
    • 如果Bean实现了InitializingBean接口,调用afterPropertiesSet()方法。
    • 调用通过@Bean(initMethod = "...")xml init-method指定的自定义初始化方法。
  5. BeanPostProcessor后置处理: 调用所有BeanPostProcessorpostProcessAfterInitialization方法。AOP代理就是在此阶段生成的
  6. 使用 (Ready to Use): Bean已就绪,存在于应用上下文中,可以被其他对象使用。
  7. 销毁 (Destruction)
    • 容器关闭时,如果Bean实现了DisposableBean接口,调用destroy()方法。
    • 调用通过@Bean(destroyMethod = "...")xml destroy-method指定的自定义销毁方法。

1.4 什么是AOP?Spring AOP有哪些实现方式?

  • AOP (Aspect-Oriented Programming)面向切面编程。它将那些与核心业务逻辑无关的横切关注点(如日志、事务、安全等)模块化,并通过“横切”的方式织入到业务逻辑中,从而减少系统的重复代码,降低模块间的耦合度

  • 核心概念

    • Aspect (切面): 横切关注点的模块化,通常是一个类(使用@Aspect注解)。
    • Advice (通知): 切面在特定连接点执行的动作(如@Before, @After, @Around等)。
    • Pointcut (切点): 匹配连接点的表达式,定义了通知何时被执行。
    • Join Point (连接点): 程序执行过程中的一个点,如方法调用、异常抛出等(Spring AOP中特指方法执行)。
  • 实现方式

    1. 基于代理(默认)
      • JDK动态代理: 针对实现了接口的目标类。Spring默认使用此方式。
      • CGLIB动态代理: 针对未实现接口的目标类。通过生成目标类的子类来实现代理。

代码示例(记录方法执行时间):

@Aspect
@Component
public class PerformanceAspect {

    // 定义切点:匹配com.example.demo.service包下所有类的所有方法
    @Pointcut("execution(* com.example.demo.service.*.*(..))")
    public void serviceLayer() {}

    // 环绕通知
    @Around("serviceLayer()")
    public Object recordTime(ProceedingJoinPoint pjp) throws Throwable {
        long startTime = System.currentTimeMillis();
        Object result = pjp.proceed(); // 执行目标方法
        long endTime = System.currentTimeMillis();
        System.out.println(pjp.getSignature() + " 方法执行耗时: " + (endTime - startTime) + "ms");
        return result;
    }
}

2. Spring MVC

2.1 请描述Spring MVC的工作流程(请求处理流程)

这是一个必考的核心题,最好能结合流程图来理解。

核心流程如下:

  1. DispatcherServlet: 作为前端控制器,接收所有请求。
  2. HandlerMappingDispatcherServlet查询一个或多个HandlerMapping,找到处理该请求的处理器(Handler / Controller)
  3. HandlerAdapterDispatcherServlet将请求交给合适的HandlerAdapter来执行具体的Controller。
  4. ControllerHandlerAdapter调用真正的Controller方法处理请求,并返回一个ModelAndView对象(包含模型数据和视图名)。
  5. ViewResolverDispatcherServlet将返回的逻辑视图名解析为具体的View对象。
  6. ViewView对象负责渲染视图(结合Model中的数据),最终生成响应内容(如HTML、JSON)。
  7. ResponseDispatcherServlet将渲染结果返回给客户端。

2.2 @RestController@Controller有什么区别?

特性@Controller@RestController
本质@Component的特殊化,标明一个类是MVC中的Controller。@Controller + @ResponseBody 的组合注解。
返回值通常返回一个视图名(String),由ViewResolver解析为物理视图。方法返回值直接通过HttpMessageConverter写入响应体,通常返回JSON/XML数据。
用途传统的前后端不分离的Web应用,返回HTML页面。前后端分离的Web应用或RESTful Web服务,提供API接口。

3. Spring Boot

3.1 Spring Boot的核心优势是什么?它解决了哪些痛点?

Spring Boot不是用来替代Spring的,而是为了更快、更简单地使用Spring

  • 优势与解决的痛点
    • 简化配置约定大于配置,提供了大量自动化配置(Auto Configuration),极大减少了繁琐的XML或Java配置。
    • 独立运行: 内嵌了Tomcat、Jetty等Servlet容器,可以打包成可执行的JAR,直接通过java -jar命令运行。
    • 简化依赖管理: 通过Starter POMs提供了一系列项目依赖的“一站式”解决方案,避免了依赖冲突。
    • 生产就绪: 提供了诸如健康检查、 metrics、外部化配置等开箱即用的生产特性(Actuator模块)。

3.2 Spring Boot的自动配置是如何实现的?

自动配置是Spring Boot的魔法之源,其核心是@EnableAutoConfiguration注解。

  1. 启动注解@SpringBootApplication注解包含了@EnableAutoConfiguration
  2. 加载配置@EnableAutoConfiguration会利用AutoConfigurationImportSelector这个类。
  3. 寻找配置: 这个类会从META-INF/spring.factories文件中读取所有预先配置好的自动配置类XXXAutoConfiguration)的全限定名。
  4. 条件装配: 这些自动配置类上都有大量的@ConditionalOnXxx注解(如@ConditionalOnClass, @ConditionalOnProperty)。Spring Boot会根据当前项目的类路径已有的Bean配置文件等条件来决定是否启用该配置。
  5. 创建Bean: 如果条件满足,配置类中定义的Bean就会被创建并加入到IoC容器中。

例如:当你引入了spring-boot-starter-web依赖(包含了Spring MVC和Tomcat的jar包),ServletWebServerFactoryAutoConfigurationDispatcherServletAutoConfiguration等配置类的条件就会满足,从而自动为你配置好内嵌Tomcat和Spring MVC。

4. Spring Cloud

4.1 微服务架构下,如何保证服务间的调用是可靠的?

在微服务网络中,服务故障是常态。Spring Cloud提供了一系列组件来提升系统的弹性(Resilience)和容错能力

  • 服务熔断(Circuit Breaker): 由 Spring Cloud CircuitBreaker (抽象) 或 Sentinel 实现。当某个目标服务调用慢或失败比例过高时,熔断器会打开,后续调用直接快速失败(fallback),不再请求目标服务。防止雪崩效应。
  • 服务降级(Fallback): 熔断器打开后或服务调用失败时,提供一种备选方案(fallback方法),返回一个托底数据或友好提示,保证主业务的可用性。
  • 负载均衡(Load Balance): 由 RibbonLoadBalancer 实现。在服务消费者端实现负载均衡,将请求分摊到多个服务实例上。
  • 服务限流(Rate Limiting): 由 Sentinel 实现。控制服务的QPS或并发线程数,防止被突发流量冲垮。

代码示例(使用@SentinelResource实现熔断降级):

@Service
public class UserService {

    @Autowired
    private RestTemplate restTemplate;

    // 定义资源点,并指定降级方法
    @SentinelResource(value = "getUserById", blockHandler = "getUserByIdBlockHandler")
    public UserDto getUserById(Long id) {
        // 模拟远程调用用户服务
        return restTemplate.getForObject("http://user-service/users/" + id, UserDto.class);
    }

    // 降级方法(参数和返回值需与原方法一致,最后加一个BlockException参数)
    public UserDto getUserByIdBlockHandler(Long id, BlockException ex) {
        // 返回托底数据
        return new UserDto(-1L, "默认用户", "服务暂时不可用,请稍后再试");
    }
}

评论 52
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值