Spring Framework 面试与学习指南
1. Spring Framework 简介
Spring Framework 是一个开源的 Java 平台,为开发企业级 Java 应用提供全面的基础设施支持。Spring 以其依赖注入(DI)和面向切面编程(AOP)等核心特性而闻名。
1.1 Spring Framework 的优势
- 轻量级:框架本身的代码量小,对应用的影响最小
- 控制反转(IoC):通过依赖注入实现松耦合,提高代码可测试性
- 面向切面编程(AOP):允许将横切关注点与业务逻辑分离
- 容器:管理应用中的对象的配置和生命周期
- 框架集成:易于与其他框架集成(如 Hibernate、MyBatis 等)
- 事务管理:统一的事务管理接口,支持声明式和编程式事务
- MVC 框架:用于开发灵活、松耦合的 Web 应用
1.2 Spring 生态体系
Spring Framework (核心)
├── Spring Boot (快速开发)
├── Spring Cloud (微服务)
├── Spring Security (安全)
├── Spring Data (数据访问)
└── Spring Batch (批处理)
2. 核心特性详解
2.1 依赖注入(DI)
依赖注入是 Spring 框架最核心的特性之一,它有助于实现松耦合的应用设计。
2.1.1 三种注入方式
1. 构造器注入(推荐)
@Component
public class UserService {
private final UserRepository userRepository;
private final EmailService emailService;
@Autowired // 可以省略,Spring 4.3+ 支持隐式注入
public UserService(UserRepository userRepository, EmailService emailService) {
this.userRepository = userRepository;
this.emailService = emailService;
}
}
2. Setter 注入
@Component
public class UserService {
private UserRepository userRepository;
private EmailService emailService;
@Autowired
public void setUserRepository(UserRepository userRepository) {
this.userRepository = userRepository;
}
@Autowired
public void setEmailService(EmailService emailService) {
this.emailService = emailService;
}
}
3. 字段注入(不推荐)
@Component
public class UserService {
@Autowired
private UserRepository userRepository;
@Autowired
private EmailService emailService;
}
2.1.2 面试常见问题
Q: 为什么推荐使用构造器注入? A:
- 保证依赖不可变性(final 修饰)
- 确保必要的依赖在对象创建时就被注入
- 便于单元测试
- 避免循环依赖问题
Q: @Autowired 和 @Resource 的区别? A:
@Autowired是 Spring 提供的注解,默认按类型注入@Resource是 JSR-250 规范,默认按名称注入@Autowired可以配合@Qualifier指定名称@Resource可以通过name属性指定名称
2.2 Spring Bean
Spring Bean 是被 Spring 容器管理的对象。
2.2.1 Bean 作用域
@Component
@Scope("singleton") // 默认作用域
public class SingletonBean {}
@Component
@Scope("prototype")
public class PrototypeBean {}
@Component
@Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
public class RequestBean {}
@Component
@Scope(value = WebApplicationContext.SCOPE_SESSION, proxyMode = ScopedProxyMode.TARGET_CLASS)
public class SessionBean {}
作用域说明:
- singleton(默认):每个 Spring IoC 容器仅创建一个实例
- prototype:每次请求都会创建新的实例
- request:每个 HTTP 请求都会创建新的实例
- session:每个 HTTP 会话都会创建新的实例
- application:ServletContext 生命周期内只有一个实例
- websocket:WebSocket 会话生命周期内只有一个实例
2.2.2 Bean 生命周期详解
Spring Bean 的实例化过程包含以下几个关键步骤:
1. 实例化 (Instantiation)
↓
2. 属性注入 (Population)
↓
3. Aware 接口回调
↓
4. BeanPostProcessor 前置处理
↓
5. 初始化 (Initialization)
↓
6. BeanPostProcessor 后置处理
↓
7. Bean 就绪 (Ready)
↓
8. 销毁 (Destruction) - 容器关闭时
详细生命周期示例:
@Component
public class LifecycleBean implements BeanNameAware, BeanFactoryAware,
InitializingBean, DisposableBean {
private String beanName;
private BeanFactory beanFactory;
public LifecycleBean() {
System.out.println("1. 构造方法执行");
}
@PostConstruct
public void postConstruct() {
System.out.println("5. @PostConstruct 执行");
}
@Override
public void setBeanName(String name) {
this.beanName = name;
System.out.println("3. BeanNameAware 回调: " + name);
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
System.out.println("3. BeanFactoryAware 回调");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("6. InitializingBean.afterPropertiesSet() 执行");
}
public void customInit() {
System.out.println("6. 自定义初始化方法执行");
}
@PreDestroy
public void preDestroy() {
System.out.println("8. @PreDestroy 执行");
}
@Override
public void destroy() throws Exception {
System.out.println("8. DisposableBean.destroy() 执行");
}
public void customDestroy() {
System.out.println("8. 自定义销毁方法执行");
}
}
面试常见问题:
Q: Bean 的生命周期中,哪些是必须的? A: 只有实例化和属性注入是必须的,其他步骤都是可选的。
Q: BeanPostProcessor 的作用是什么? A: BeanPostProcessor 是 Spring 提供的扩展点,可以在 Bean 初始化前后进行自定义处理,常用于 AOP 代理的创建。
Q: 如何自定义 Bean 的初始化方法? A: 可以通过 @PostConstruct 注解、实现 InitializingBean 接口,或在 XML 中配置 init-method 属性。
2.3 循环依赖问题
2.3.1 循环依赖的解决方案
Spring 通过三级缓存解决循环依赖:
@Service
public class AService {
@Autowired
private BService bService;
public void methodA() {
bService.methodB();
}
}
@Service
public class BService {
@Autowired
private AService aService;
public void methodB() {
aService.methodA();
}
}
三级缓存机制:
- 一级缓存:
singletonObjects- 存放完全初始化好的 Bean - 二级缓存:
earlySingletonObjects- 存放早期暴露的 Bean(未完全初始化) - 三级缓存:
singletonFactories- 存放 Bean 的工厂对象
2.3.2 面试常见问题
Q: Spring 如何解决循环依赖? A: Spring 通过三级缓存机制解决循环依赖:
- 创建 A 的实例,放入三级缓存
- 注入 B 时,发现 B 不存在,创建 B 的实例
- B 注入 A 时,从三级缓存中获取 A 的早期引用
- B 创建完成,放入一级缓存
- A 继续完成初始化,放入一级缓存
Q: 构造器注入的循环依赖能解决吗? A: 不能。Spring 只能解决 setter 注入和字段注入的循环依赖,构造器注入的循环依赖无法解决。
3. Spring IoC 容器
IoC 容器是 Spring 框架的核心。它负责管理对象的创建、配置和组装。
3.1 配置方式对比
3.1.1 XML 配置(传统方式)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 开启注解扫描 -->
<context:component-scan base-package="com.example"/>
<!-- 配置数据源 -->
<bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/test"/>
<property name="username" value="root"/>
<property name="password" value="password"/>
</bean>
<!-- 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 开启事务注解 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
</beans>
3.1.2 Java 配置(推荐)
@Configuration
@ComponentScan("com.example")
@EnableTransactionManagement
@PropertySource("classpath:application.properties")
public class AppConfig {
@Value("${db.driver}")
private String driverClassName;
@Value("${db.url}")
private String jdbcUrl;
@Value("${db.username}")
private String username;
@Value("${db.password}")
private String password;
@Bean
public DataSource dataSource() {
HikariDataSource dataSource = new HikariDataSource();
dataSource.setDriverClassName(driverClassName);
dataSource.setJdbcUrl(jdbcUrl);
dataSource.setUsername(username);
dataSource.setPassword(password);
return dataSource;
}
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
@Bean
public JdbcTemplate jdbcTemplate(DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
}
3.1.3 注解配置(最常用)
@SpringBootApplication
@EnableTransactionManagement
@EnableCaching
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
@Service
@Transactional
public class UserService {
@Autowired
private UserRepository userRepository;
@Cacheable("users")
public User findById(Long id) {
return userRepository.findById(id);
}
}
3.2 面试常见问题
Q: @Configuration 和 @Component 的区别? A:
@Configuration标识的类会被 CGLIB 增强,确保@Bean方法返回单例@Component是普通的组件注解,不会被增强@Configuration类中的@Bean方法可以相互调用
Q: @ComponentScan 的作用是什么? A: @ComponentScan 用于扫描指定包下的组件,将带有 @Component、@Service、@Repository、@Controller 注解的类注册为 Spring Bean。
Q: 如何排除某些 Bean 的自动装配? A: 可以使用 @ConditionalOnMissingBean、@ConditionalOnProperty 等条件注解,或者在 @ComponentScan 中使用 excludeFilters。
4. Spring AOP
AOP(面向切面编程)允许将横切关注点(如日志、事务、安全等)与业务逻辑分离。
4.1 AOP 核心概念
- Aspect(切面):横切关注点的模块化
- Join point(连接点):程序执行过程中的某个特定点
- Pointcut(切入点):匹配连接点的表达式
- Advice(通知):在切入点执行的代码
- Target Object(目标对象):被代理的对象
- Proxy(代理):AOP 框架创建的对象
4.2 通知类型
@Aspect
@Component
public class LoggingAspect {
// 前置通知:在目标方法执行前执行
@Before("execution(* com.example.service.*.*(..))")
public void logBefore(JoinPoint joinPoint) {
String methodName = joinPoint.getSignature().getName();
System.out.println("方法 " + methodName + " 开始执行");
}
// 后置通知:在目标方法执行后执行(无论是否异常)
@After("execution(* com.example.service.*.*(..))")
public void logAfter(JoinPoint joinPoint) {
String methodName = joinPoint.getSignature().getName();
System.out.println("方法 " + methodName + " 执行完成");
}
// 返回通知:在目标方法正常返回后执行
@AfterReturning(pointcut = "execution(* com.example.service.*.*(..))",
returning = "result")
public void logAfterReturning(JoinPoint joinPoint, Object result) {
String methodName = joinPoint.getSignature().getName();
System.out.println("方法 " + methodName + " 返回结果: " + result);
}
// 异常通知:在目标方法抛出异常后执行
@AfterThrowing(pointcut = "execution(* com.example.service.*.*(..))",
throwing = "ex")
public void logAfterThrowing(JoinPoint joinPoint, Exception ex) {
String methodName = joinPoint.getSignature().getName();
System.out.println("方法 " + methodName + " 抛出异常: " + ex.getMessage());
}
// 环绕通知:在目标方法执行前后都执行
@Around("execution(* com.example.service.*.*(..))")
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
String methodName = joinPoint.getSignature().getName();
long startTime = System.currentTimeMillis();
try {
Object result = joinPoint.proceed();
long endTime = System.currentTimeMillis();
System.out.println("方法 " + methodName + " 执行时间: " + (endTime - startTime) + "ms");
return result;
} catch (Exception e) {
System.out.println("方法 " + methodName + " 执行异常");
throw e;
}
}
}
4.3 切入点表达式
@Aspect
@Component
public class PointcutExamples {
// 匹配所有 public 方法
@Pointcut("execution(public * *(..))")
public void publicMethods() {}
// 匹配特定包下的所有方法
@Pointcut("execution(* com.example.service.*.*(..))")
public void serviceMethods() {}
// 匹配带有特定注解的方法
@Pointcut("@annotation(org.springframework.transaction.annotation.Transactional)")
public void transactionalMethods() {}
// 匹配特定参数类型的方法
@Pointcut("execution(* *(String, int))")
public void stringIntMethods() {}
// 组合切入点
@Pointcut("serviceMethods() && publicMethods()")
public void publicServiceMethods() {}
@Before("publicServiceMethods()")
public void beforePublicService() {
System.out.println("执行公共服务方法");
}
}
4.4 面试常见问题
Q: Spring AOP 的代理方式有哪些? A:
- JDK 动态代理:基于接口的代理,目标类必须实现接口
- CGLIB 代理:基于继承的代理,可以代理没有实现接口的类
- Spring 默认优先使用 JDK 动态代理,如果目标类没有实现接口则使用 CGLIB
Q: AOP 的底层原理是什么? A: AOP 的底层原理是动态代理:
- 在运行时动态创建代理对象
- 代理对象拦截目标方法的调用
- 在方法调用前后执行切面逻辑
Q: @Aspect 和 @Component 的区别? A: @Aspect 标识这是一个切面类,@Component 标识这是一个 Spring 组件。通常两者结合使用,让切面类被 Spring 容器管理。
5. Spring MVC
Spring MVC 是一个用于构建 Web 应用的框架,它实现了 MVC 设计模式。
5.1 Spring MVC 核心组件
HTTP 请求
↓
DispatcherServlet (前端控制器)
↓
HandlerMapping (处理器映射)
↓
HandlerAdapter (处理器适配器)
↓
Controller (控制器)
↓
ViewResolver (视图解析器)
↓
View (视图)
↓
HTTP 响应
5.2 控制器开发
@Controller
@RequestMapping("/api/users")
@Validated
public class UserController {
private final UserService userService;
public UserController(UserService userService) {
this.userService = userService;
}
// GET 请求 - 获取用户列表
@GetMapping
public ResponseEntity<List<User>> getUsers(
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "10") int size) {
List<User> users = userService.findAll(page, size);
return ResponseEntity.ok(users);
}
// GET 请求 - 获取单个用户
@GetMapping("/{id}")
public ResponseEntity<User> getUser(@PathVariable Long id) {
User user = userService.findById(id);
if (user == null) {
return ResponseEntity.notFound().build();
}
return ResponseEntity.ok(user);
}
// POST 请求 - 创建用户
@PostMapping
public ResponseEntity<User> createUser(@Valid @RequestBody User user) {
User createdUser = userService.create(user);
return ResponseEntity.status(HttpStatus.CREATED).body(createdUser);
}
// PUT 请求 - 更新用户
@PutMapping("/{id}")
public ResponseEntity<User> updateUser(@PathVariable Long id,
@Valid @RequestBody User user) {
User updatedUser = userService.update(id, user);
if (updatedUser == null) {
return ResponseEntity.notFound().build();
}
return ResponseEntity.ok(updatedUser);
}
// DELETE 请求 - 删除用户
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
boolean deleted = userService.delete(id);
if (!deleted) {
return ResponseEntity.notFound().build();
}
return ResponseEntity.noContent().build();
}
// 异常处理
@ExceptionHandler(UserNotFoundException.class)
public ResponseEntity<ErrorResponse> handleUserNotFound(UserNotFoundException ex) {
ErrorResponse error = new ErrorResponse("USER_NOT_FOUND", ex.getMessage());
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(error);
}
}
5.3 数据绑定和验证
@Data
@Validated
public class User {
@NotNull(message = "用户ID不能为空")
private Long id;
@NotBlank(message = "用户名不能为空")
@Size(min = 2, max = 50, message = "用户名长度必须在2-50之间")
private String username;
@Email(message = "邮箱格式不正确")
private String email;
@Min(value = 0, message = "年龄不能为负数")
@Max(value = 150, message = "年龄不能超过150")
private Integer age;
}
@RestController
@RequestMapping("/api/users")
public class UserController {
@PostMapping
public ResponseEntity<User> createUser(@Valid @RequestBody User user,
BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
List<String> errors = bindingResult.getFieldErrors()
.stream()
.map(FieldError::getDefaultMessage)
.collect(Collectors.toList());
throw new ValidationException("验证失败: " + errors);
}
User createdUser = userService.create(user);
return ResponseEntity.status(HttpStatus.CREATED).body(createdUser);
}
}
5.4 面试常见问题
Q: @Controller 和 @RestController 的区别? A:
@Controller标识这是一个控制器,返回视图名称@RestController是@Controller和@ResponseBody的组合,直接返回数据
Q: Spring MVC 的请求处理流程是什么? A:
- 请求到达 DispatcherServlet
- DispatcherServlet 通过 HandlerMapping 找到对应的 Handler
- HandlerAdapter 调用 Handler 处理请求
- Handler 返回 ModelAndView
- ViewResolver 解析视图
- 渲染视图并返回响应
Q: 如何自定义异常处理? A: 可以使用 @ExceptionHandler 注解处理特定异常,或使用 @ControllerAdvice 创建全局异常处理器。
6. Spring 事务管理
Spring 提供了一致的事务管理抽象,支持声明式事务和编程式事务。
6.1 事务传播行为
@Service
@Transactional
public class UserService {
@Autowired
private UserRepository userRepository;
@Autowired
private OrderService orderService;
// REQUIRED(默认):支持当前事务,如果不存在则创建新事务
@Transactional(propagation = Propagation.REQUIRED)
public void createUser(User user) {
userRepository.save(user);
// 如果这里抛出异常,整个事务回滚
}
// REQUIRES_NEW:总是创建新事务
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void createUserInNewTransaction(User user) {
userRepository.save(user);
// 即使外层事务回滚,这个事务也会提交
}
// SUPPORTS:支持当前事务,如果不存在则以非事务方式执行
@Transactional(propagation = Propagation.SUPPORTS)
public User findUser(Long id) {
return userRepository.findById(id);
}
// NOT_SUPPORTED:以非事务方式执行
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void sendEmail(String email) {
// 发送邮件,不参与事务
}
// MANDATORY:必须在事务中执行,否则抛出异常
@Transactional(propagation = Propagation.MANDATORY)
public void updateUser(User user) {
userRepository.save(user);
}
// NEVER:不能在事务中执行,否则抛出异常
@Transactional(propagation = Propagation.NEVER)
public void readOnlyOperation() {
// 只读操作
}
// NESTED:如果存在事务则在嵌套事务中执行
@Transactional(propagation = Propagation.NESTED)
public void nestedOperation() {
// 嵌套事务操作
}
}
6.2 事务隔离级别
@Service
public class TransactionIsolationService {
// 读未提交:允许脏读
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
public void readUncommitted() {
// 可以读取其他事务未提交的数据
}
// 读已提交:防止脏读
@Transactional(isolation = Isolation.READ_COMMITTED)
public void readCommitted() {
// 只能读取已提交的数据
}
// 可重复读:防止不可重复读
@Transactional(isolation = Isolation.REPEATABLE_READ)
public void repeatableRead() {
// 在同一事务中多次读取同一数据,结果一致
}
// 串行化:最高隔离级别
@Transactional(isolation = Isolation.SERIALIZABLE)
public void serializable() {
// 完全串行化执行,性能最低
}
}
6.3 声明式事务
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
@Autowired
private OrderRepository orderRepository;
// 基本事务
@Transactional
public void createUser(User user) {
userRepository.save(user);
}
// 只读事务
@Transactional(readOnly = true)
public User findUser(Long id) {
return userRepository.findById(id);
}
// 指定超时时间
@Transactional(timeout = 30)
public void longRunningOperation() {
// 30秒超时
}
// 指定回滚异常
@Transactional(rollbackFor = {UserException.class, OrderException.class})
public void createUserWithOrder(User user, Order order) {
userRepository.save(user);
orderRepository.save(order);
// 如果抛出 UserException 或 OrderException,事务回滚
}
// 指定不回滚异常
@Transactional(noRollbackFor = {ValidationException.class})
public void createUserWithValidation(User user) {
userRepository.save(user);
// 如果抛出 ValidationException,事务不回滚
}
}
6.4 编程式事务
6.4.1 使用 TransactionTemplate
@Service
public class UserService {
private final TransactionTemplate transactionTemplate;
private final UserRepository userRepository;
public UserService(TransactionTemplate transactionTemplate, UserRepository userRepository) {
this.transactionTemplate = transactionTemplate;
this.userRepository = userRepository;
}
public void createUserWithTransaction(User user) {
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
try {
userRepository.save(user);
// 其他业务逻辑
} catch (Exception e) {
status.setRollbackOnly();
throw e;
}
}
});
}
// 使用 Lambda 表达式的简化写法
public void createUserWithLambda(User user) {
transactionTemplate.execute(status -> {
userRepository.save(user);
return null;
});
}
// 带返回值的事务
public User createUserAndReturn(User user) {
return transactionTemplate.execute(status -> {
User savedUser = userRepository.save(user);
return savedUser;
});
}
}
6.4.2 使用 PlatformTransactionManager
@Service
public class ComplexTransactionService {
private final PlatformTransactionManager transactionManager;
private final UserRepository userRepository;
public ComplexTransactionService(PlatformTransactionManager transactionManager,
UserRepository userRepository) {
this.transactionManager = transactionManager;
this.userRepository = userRepository;
}
public void complexTransaction(User user, Order order) {
// 定义事务属性
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
def.setTimeout(30); // 30秒超时
def.setReadOnly(false);
TransactionStatus status = transactionManager.getTransaction(def);
try {
// 执行业务逻辑
userRepository.save(user);
// 其他操作...
// 提交事务
transactionManager.commit(status);
} catch (Exception e) {
// 回滚事务
transactionManager.rollback(status);
throw e;
}
}
}
6.5 面试常见问题
Q: 事务的 ACID 特性是什么? A:
- 原子性(Atomicity):事务是不可分割的工作单位
- 一致性(Consistency):事务执行前后数据保持一致
- 隔离性(Isolation):并发事务之间相互隔离
- 持久性(Durability):事务提交后数据永久保存
Q: 事务传播行为有哪些? A:
REQUIRED:支持当前事务,如果不存在则创建新事务REQUIRES_NEW:总是创建新事务SUPPORTS:支持当前事务,如果不存在则以非事务方式执行NOT_SUPPORTED:以非事务方式执行MANDATORY:必须在事务中执行NEVER:不能在事务中执行NESTED:如果存在事务则在嵌套事务中执行
Q: 事务隔离级别有哪些? A:
READ_UNCOMMITTED:读未提交,允许脏读READ_COMMITTED:读已提交,防止脏读REPEATABLE_READ:可重复读,防止不可重复读SERIALIZABLE:串行化,最高隔离级别
Q: 声明式事务和编程式事务的区别? A:
- 声明式事务:通过注解配置,代码侵入性小,推荐使用
- 编程式事务:通过代码控制,灵活性高,适用于复杂场景
