Skip to content

Spring 面试题

Spring 基础

Q1: Spring 框架的核心模块有哪些?

┌─────────────────────────────────────────────────────────────────┐
│                      Spring 核心模块                              │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│   Spring Core                                                    │
│   ├── Core - IoC 容器、依赖注入                                 │
│   ├── Context - 事件机制、资源加载                               │
│   └── Beans - BeanFactory 核心                                  │
│                                                                  │
│   Spring AOP                                                    │
│   └── 面向切面编程、事务管理                                      │
│                                                                  │
│   Spring MVC                                                     │
│   └── Web 请求处理、视图解析                                     │
│                                                                  │
│   Spring Data                                                    │
│   ├── JDBC                                                       │
│   ├── ORM (Hibernate/JPA)                                       │
│   └── Nosql (Redis/MongoDB)                                     │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘

答案

  • Core Container:IoC/DI 核心模块
  • AOP:面向切面编程
  • MVC:Web 层框架
  • DAO:JDBC 封装
  • ORM:对象关系映射
  • Context:应用上下文
  • Web:Web 支持模块

Q2: 什么是 Spring IoC 容器?

答案: IoC(Inversion of Control,控制反转)是 Spring 的核心。

原理

  1. 控制反转:对象的创建和管理由容器负责,而不是应用代码
  2. 依赖注入:容器在运行期将依赖关系注入到对象中
java
// 传统方式:应用代码自己创建对象
UserService service = new UserService();

// IoC 方式:容器创建对象并注入
// 容器:UserService service = new UserService();
//       service.setUserRepository(userRepository);

IoC 容器的好处

  • 降低对象间的耦合度
  • 便于单元测试(可以用 Mock 对象)
  • 对象的生命周期由容器管理

Q3: BeanFactory 和 ApplicationContext 的区别?

特性BeanFactoryApplicationContext
初始化延迟加载预加载
功能基础功能更强大(事件、国际化等)
自动装配支持支持
BeanPostProcessor需手动注册自动注册
国际化不支持支持
Web 应用不适用支持

答案

java
// BeanFactory - 延迟加载,第一次 getBean 时才创建
BeanFactory factory = new XmlBeanFactory(new ClassPathResource("beans.xml"));
UserService service = factory.getBean("userService");

// ApplicationContext - 预加载,启动时就创建所有单例 Bean
ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
UserService service = ctx.getBean("userService");

实际开发中:通常使用 ApplicationContext。


Q4: Spring Bean 的生命周期?

┌─────────────────────────────────────────────────────────────────┐
│                      Bean 生命周期                               │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│  1. 实例化 (Instantiation)                                      │
│            ↓                                                     │
│  2. 属性赋值 (Populate Properties)                               │
│            ↓                                                     │
│  3. BeanNameAware                                               │
│            ↓                                                     │
│  4. BeanFactoryAware / ApplicationContextAware                  │
│            ↓                                                     │
│  5. BeanPostProcessor.beforeInitialization                     │
│            ↓                                                     │
│  6. @PostConstruct / InitializingBean                           │
│            ↓                                                     │
│  7. BeanPostProcessor.afterInitialization                      │
│            ↓                                                     │
│  8. 使用中                                                       │
│            ↓                                                     │
│  9. @PreDestroy / DisposableBean                              │
│            ↓                                                     │
│  10. 销毁 (Destruction)                                         │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘

代码示例

java
@Component
public class UserService implements BeanNameAware,
                                      InitializingBean,
                                      DisposableBean {

    private String beanName;

    @Override
    public void setBeanName(String name) {
        this.beanName = name;
        System.out.println("Bean 名称:" + name);
    }

    @PostConstruct
    public void init() {
        System.out.println("@PostConstruct 初始化");
    }

    @Override
    public void afterPropertiesSet() {
        System.out.println("InitializingBean 初始化");
    }

    @PreDestroy
    public void cleanup() {
        System.out.println("@PreDestroy 销毁");
    }

    @Override
    public void destroy() {
        System.out.println("DisposableBean 销毁");
    }
}

Q5: Spring Bean 的作用域有哪些?

作用域说明是否需要 Web 环境
singleton单例(默认),容器中只有一个实例
prototype原型,每次获取创建新实例
request每次 HTTP 请求创建一个实例
session同一个 HTTP Session 共享一个实例
application整个 Web 应用只有一个实例
websocketWebSocket 生命周期内只有一个实例
java
// 单例(默认)
@Scope("singleton")

// 原型:每次获取新实例
@Scope("prototype")

// request:每个请求一个实例
@Scope("request")

// session:每个会话一个实例
@Scope("session")

面试点

  • 单例 Bean 是线程不安全的!
  • 原型 Bean 的销毁需要手动处理
  • 单例 Bean 依赖原型 Bean 时,原型 Bean 不会每次都创建新实例(需要用 @LookupObjectFactory

Spring Boot

Q6: Spring Boot 自动配置原理?

┌─────────────────────────────────────────────────────────────────┐
│                    自动配置执行流程                               │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│  1. @SpringBootApplication 启动                                 │
│            ↓                                                     │
│  2. @EnableAutoConfiguration 启用自动配置                        │
│            ↓                                                     │
│  3. SpringFactoriesLoader 加载 spring.factories                 │
│            ↓                                                     │
│  4. 根据 @Conditional 条件筛选配置类                            │
│            ↓                                                     │
│  5. 通过 @Bean 注册组件                                         │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘

核心注解

java
@SpringBootApplication
// 等价于:
@Configuration          // 配置类
@EnableAutoConfiguration // 启用自动配置
@ComponentScan          // 组件扫描

spring.factories

properties
# META-INF/spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration

条件注解

  • @ConditionalOnClass - 类路径存在才配置
  • @ConditionalOnMissingBean - 不存在 Bean 时才配置
  • @ConditionalOnProperty - 配置属性满足条件才配置

Q7: Spring Boot 常用起步依赖(Starter)?

Starter用途
spring-boot-starter-webWeb 开发(Tomcat + Spring MVC)
spring-boot-starter-data-redisRedis 支持
spring-boot-starter-data-jpaJPA 持久层
spring-boot-starter-security安全认证
spring-boot-starter-actuator应用监控
spring-boot-starter-test单元测试
spring-boot-starter-validation参数校验

Q8: Spring Boot 如何实现多环境配置?

方式1:多文件配置

application.yml              # 公共配置
application-dev.yml          # 开发环境
application-test.yml         # 测试环境
application-prod.yml        # 生产环境

方式2:命令行指定

bash
java -jar app.jar --spring.profiles.active=prod

方式3:IDEA 配置 在 Run Configuration 中设置 VM options: -Dspring.profiles.active=dev


Q9: @SpringBootApplication 做了哪些事?

java
@SpringBootApplication
public class MyApplication {
    public static void main(String[] args) {
        SpringApplication.run(MyApplication.class, args);
    }
}

等价于:

java
@Configuration           // 标注为配置类
@EnableAutoConfiguration  // 启用自动配置
@ComponentScan           // 组件扫描(默认扫描当前包及其子包)

exclude 属性

java
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})

Spring MVC

Q10: Spring MVC 请求处理流程?

┌─────────────────────────────────────────────────────────────────┐
│                      Spring MVC 请求处理流程                      │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│  请求 ──→ DispatcherServlet                                     │
│              ↓                                                   │
│         HandlerMapping 查找 Handler                               │
│              ↓                                                   │
│         HandlerAdapter 执行 Handler                              │
│              ↓                                                   │
│         返回 ModelAndView                                        │
│              ↓                                                   │
│         ViewResolver 解析视图                                    │
│              ↓                                                   │
│         View 渲染视图                                            │
│              ↓                                                   │
│         响应                                                     │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘

Q11: @Controller 和 @RestController 的区别?

java
// 普通 Controller
@Controller
public class UserController {
    @RequestMapping("/user")
    public String getUser(Model model) {
        model.addAttribute("user", user);
        return "userDetail";  // 返回视图名
    }
}

// REST Controller
@RestController
@RequestMapping("/api/users")
public class UserApiController {
    @GetMapping("/{id}")
    public User getUser(@PathVariable Long id) {
        return userService.getUser(id);  // 直接返回数据
    }
}

区别

  • @RestController = @Controller + @ResponseBody
  • @Controller 返回视图(JSP/Freemarker)
  • @RestController 返回 JSON/XML 数据

Q12: @RequestMapping 的常用属性?

java
@RestController
@RequestMapping("/api/users")
public class UserController {

    @RequestMapping(
        value = "/{id}",
        method = RequestMethod.GET,        // 请求方法
        params = "version=1",              // 请求参数
        headers = "Content-Type=application/json", // 请求头
        consumes = "application/json",     // 消费的内容类型
        produces = "application/json"      // 生产的内容类型
    )
    public User getUser(@PathVariable Long id) {
        return userService.getUser(id);
    }

    // 简写形式
    @GetMapping("/{id}")      // GET 请求
    @PostMapping             // POST 请求
    @PutMapping("/{id}")      // PUT 请求
    @DeleteMapping("/{id}")   // DELETE 请求
}

Q13: Spring MVC 参数绑定的注解有哪些?

java
@GetMapping("/user")
public User getUser(
    @PathVariable Long id,                    // 路径参数
    @RequestParam String name,                // 请求参数
    @RequestParam(defaultValue = "10") int pageSize,  // 默认值
    @RequestParam(required = false) String email,       // 可选参数
    @RequestHeader String authorization,       // 请求头
    @CookieValue("JSESSIONID") String sessionId,  // Cookie
    @RequestBody User user,                   // 请求体(JSON)
    @ModelAttribute User user                  // 表单参数
) { ... }

Q14: Spring MVC 如何做参数校验?

步骤1:添加依赖

xml
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
</dependency>

步骤2:在实体类上加校验注解

java
public class UserRequest {
    @NotBlank(message = "用户名不能为空")
    @Size(min = 3, max = 20, message = "用户名长度3-20")
    private String name;

    @NotNull(message = "年龄不能为空")
    @Min(value = 0, message = "年龄最小0")
    @Max(value = 150, message = "年龄最大150")
    private Integer age;

    @Email(message = "邮箱格式不正确")
    private String email;

    @Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式不正确")
    private String phone;
}

步骤3:Controller 接收校验

java
@PostMapping
public User createUser(@Valid @RequestBody UserRequest request) {
    // 校验失败会抛出 MethodArgumentNotValidException
    return userService.createUser(request);
}

步骤4:全局异常处理

java
@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Result handleValidException(MethodArgumentNotValidException e) {
        String message = e.getBindingResult().getFieldError().getDefaultMessage();
        return Result.error(400, message);
    }
}

Q15: Spring MVC 拦截器(Interceptor)和 Filter 的区别?

对比FilterInterceptor
来源Servlet 规范Spring MVC
作用范围所有请求Spring MVC 请求
配置位置web.xml / @WebFilter@Configuration
执行顺序FilterChainHandlerInterceptor
能否获取 Spring Bean不能(需通过 Servlet)

Filter 示例

java
@WebFilter("/*")
public class MyFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response,
                         FilterChain chain) throws IOException, ServletException {
        // 前置处理
        HttpServletRequest req = (HttpServletRequest) request;
        System.out.println("Filter: " + req.getRequestURI());

        chain.doFilter(request, response);  // 继续执行

        // 后置处理
    }
}

Interceptor 示例

java
@Component
public class LoginInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request,
                           HttpServletResponse response,
                           Object handler) throws Exception {
        // 前置处理
        String token = request.getHeader("Authorization");
        if (token == null || !tokenService.validate(token)) {
            response.setStatus(401);
            return false;
        }
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request,
                          HttpServletResponse response,
                          Object handler,
                          ModelAndView modelAndView) {
        // 后置处理
    }

    @Override
    public void afterCompletion(HttpServletRequest request,
                               HttpServletResponse response,
                               Object handler,
                               Exception ex) {
        // 整个请求完成后
    }
}

Spring 事务

Q16: Spring 事务的隔离级别?

隔离级别说明脏读不可重复读幻读
DEFAULT使用数据库默认---
READ_UNCOMMITTED未提交读
READ_COMMITTED已提交读
REPEATABLE_READ可重复读
SERIALIZABLE串行化
java
@Transactional(isolation = Isolation.READ_COMMITTED)
public void updateUser(User user) {
    // 事务处理
}

Q17: Spring 事务的传播行为?

传播行为说明
REQUIRED如果当前有事务,加入该事务(默认)
REQUIRES_NEW开启新事务,挂起当前事务
SUPPORTS如果当前有事务,加入该事务
NOT_SUPPORTED不使用事务,挂起当前事务
MANDATORY必须在事务中执行,否则抛异常
NEVER必须在非事务中执行,否则抛异常
NESTED嵌套事务(Savepoint)

示例

java
@Transactional
public void outerMethod() {
    // 这是一个事务

    innerMethod();  // 默认 REQUIRED,会加入当前事务

    innerMethod2();  // REQUIRES_NEW,会开启新事务
}

@Transactional(propagation = Propagation.REQUIRES_NEW)
public void innerMethod2() {
    // 这是另一个独立的事务
}

Q18: @Transactional 失效的场景?

1. 方法内部调用

java
@Service
public class UserService {

    public void outer() {
        // ❌ 失效!this.inner() 没有经过代理对象
        this.inner();
    }

    @Transactional
    public void inner() {
        // 不会开启事务
    }
}

// ✅ 正确做法
@Autowired
private UserService self;
public void outer() {
    self.inner();
}

2. 异常被 catch 吞掉

java
@Transactional
public void update() {
    try {
        doSomething();
    } catch (Exception e) {
        // ❌ 异常被吞掉,事务不会回滚
    }
}

3. 非 public 方法

java
// ❌ 失效
@Transactional
private void update() { }

// ✅ 正确
@Transactional
public void update() { }

4. rollbackFor 配置错误

java
// 默认只对 RuntimeException 回滚
@Transactional
public void update() {
    throw new IOException();  // ❌ 不会回滚
}

// ✅ 指定回滚异常
@Transactional(rollbackFor = Exception.class)
public void update() throws IOException {
    throw new IOException();  // ✅ 会回滚
}

5. 多数据源

java
// @Transactional 默认作用于主数据源
// 如果操作从数据源,需要指定
@Transactional(transactionManager = "txManager2")
public void updateSlave() { }

Spring 疑难

Q19: Spring 如何解决循环依赖?

┌─────────────────────────────────────────────────────────────────┐
│                      循环依赖示意                                 │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│   A → @Autowired B                                              │
│   B → @Autowired A                                              │
│                                                                  │
│   Spring 通过三级缓存解决:                                       │
│                                                                  │
│   singletonObjects ─── 一级缓存(成品Bean)                      │
│   earlySingletonObjects ─── 二级缓存(早期Bean)                 │
│   singletonFactories ─── 三级缓存(BeanFactory)                 │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘

解决过程

  1. 创建 A,发现依赖 B,尝试从三级缓存获取 B 的 ObjectFactory
  2. 创建 B,发现依赖 A,尝试从三级缓存获取 A 的 ObjectFactory
  3. A 从三级缓存获取到 ObjectFactory,创建早期 A(未填充属性)
  4. B 获取到早期 A,填充属性,创建完成
  5. A 获取到完成的 B,填充属性,创建完成

不能解决的情况

  • @Prototype 作用域的 Bean
  • 构造函数注入的循环依赖
  • @Async 注解的 Bean

Q20: Spring AOP 的实现原理?

┌─────────────────────────────────────────────────────────────────┐
│                      AOP 代理实现                                 │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│   Spring AOP                                                    │
│        │                                                        │
│        ├── JDK 动态代理(目标类实现接口)                         │
│        │   └── Proxy.newProxyInstance()                        │
│        │                                                        │
│        └── CGLIB 代理(目标类没实现接口)                        │
│            └── Enhancer.create()                               │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘

示例

java
@Component
@Aspect
public class LogAspect {

    @Pointcut("execution(* com.example.service.*.*(..))")
    public void pointcut() { }

    @Around("pointcut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        long start = System.currentTimeMillis();

        // 前置通知
        System.out.println("方法执行前");

        Object result = joinPoint.proceed();  // 执行目标方法

        // 后置通知
        long end = System.currentTimeMillis();
        System.out.println("方法执行后,耗时:" + (end - start) + "ms");

        return result;
    }

    @Before("pointcut()")
    public void before() {
        System.out.println("前置通知");
    }

    @AfterReturning(pointcut = "pointcut()", returning = "result")
    public void afterReturning(Object result) {
        System.out.println("返回通知:" + result);
    }

    @AfterThrowing(pointcut = "pointcut()", throwing = "e")
    public void afterThrowing(Exception e) {
        System.out.println("异常通知:" + e.getMessage());
    }
}

Q21: Spring Cloud 和 Spring Boot 的关系?

对比Spring BootSpring Cloud
功能快速构建应用分布式系统解决方案
定位单体应用微服务架构
依赖基础框架整合多个组件
关系Spring Boot 是 Spring Cloud 的基础

Spring Boot + Spring Cloud 组合

┌─────────────────────────────────────────────────────────────────┐
│                      微服务技术栈                                 │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│   Spring Boot - 快速开发单个微服务                               │
│        │                                                         │
│        ├── 服务开发 (Spring MVC / WebFlux)                      │
│        ├── 服务注册与发现 (Nacos / Eureka)                       │
│        ├── 服务调用 (OpenFeign / Dubbo)                          │
│        ├── 负载均衡 (Ribbon / LoadBalancer)                     │
│        ├── 熔断器 (Sentinel / Hystrix)                          │
│        ├── 网关 (Gateway)                                       │
│        ├── 配置中心 (Nacos Config / Spring Cloud Config)         │
│        └── 消息总线 (Spring Cloud Bus)                           │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘

总结

Spring 高频面试知识点

知识点面试频率
IoC/DI 原理⭐⭐⭐⭐⭐
Bean 生命周期⭐⭐⭐⭐
Spring Boot 自动配置⭐⭐⭐⭐⭐
@Transactional 失效场景⭐⭐⭐⭐⭐
事务隔离级别和传播行为⭐⭐⭐⭐
Spring MVC 请求处理流程⭐⭐⭐⭐
循环依赖解决方案⭐⭐⭐⭐
AOP 原理和实现⭐⭐⭐⭐