Spring 面试宝典
目录
- Spring 基础概念
- IoC 容器
- 依赖注入
- AOP 面向切面编程
- Spring MVC
- Spring Boot
- Spring Security
- Spring Data
- Spring Cloud
- 事务管理
- Bean 生命周期
- 高级特性
Spring 基础概念
1. 什么是 Spring 框架?
答案:
Spring 是一个轻量级的开源 Java 框架,用于构建企业级应用程序。它提供了全面的编程和配置模型,支持 Java 应用程序的快速开发。
核心特性:
- 依赖注入 (DI)
- 面向切面编程 (AOP)
- 声明式事务管理
- MVC 框架
- 数据访问抽象
2. Spring 框架有哪些核心模块?
答案:
- Spring Core Container: IoC 容器,Bean 工厂
- Spring AOP: 面向切面编程
- Spring Context: 应用上下文
- Spring DAO: 数据访问对象
- Spring ORM: 对象关系映射
- Spring Web: Web 相关功能
- Spring MVC: 模型-视图-控制器
3. Spring 的优势是什么?
答案:
- 轻量级: 非侵入性,对应用代码影响小
- IoC 容器: 管理对象生命周期和依赖关系
- AOP 支持: 横切关注点的模块化
- 声明式事务: 简化事务管理
- 测试友好: 便于单元测试和集成测试
- 丰富的生态系统: 大量第三方集成
IoC 容器
4. 什么是 IoC(控制反转)?
答案:
IoC (Inversion of Control) 是一种设计原则,将对象的创建和依赖关系的管理从应用程序代码中转移到外部容器。
传统方式:
public class UserService {private UserDao userDao = new UserDaoImpl(); // 硬编码依赖
}
IoC 方式:
public class UserService {private UserDao userDao;public UserService(UserDao userDao) { // 依赖注入this.userDao = userDao;}
}
5. Spring IoC 容器有哪些类型?
答案:
- BeanFactory: 基础容器接口,延迟加载
- ApplicationContext: 高级容器,预加载所有单例 Bean
主要实现类:
ClassPathXmlApplicationContext
FileSystemXmlApplicationContext
AnnotationConfigApplicationContext
WebApplicationContext
6. BeanFactory 和 ApplicationContext 的区别?
答案:
特性 | BeanFactory | ApplicationContext |
---|---|---|
Bean 实例化 | 延迟加载 | 预加载单例 Bean |
国际化 | 不支持 | 支持 |
事件发布 | 不支持 | 支持 |
自动 BeanPostProcessor | 不支持 | 支持 |
自动 BeanFactoryPostProcessor | 不支持 | 支持 |
性能 | 更快启动 | 启动较慢但功能丰富 |
依赖注入
7. 什么是依赖注入(DI)?
答案:
依赖注入是 IoC 的一种实现方式,通过外部容器将依赖对象注入到目标对象中。
三种注入方式:
- 构造器注入
- Setter 注入
- 字段注入
8. 构造器注入和 Setter 注入的区别?
答案:
构造器注入:
@Service
public class UserService {private final UserDao userDao;public UserService(UserDao userDao) {this.userDao = userDao;}
}
Setter 注入:
@Service
public class UserService {private UserDao userDao;@Autowiredpublic void setUserDao(UserDao userDao) {this.userDao = userDao;}
}
区别:
- 构造器注入:强制依赖,不可变,推荐使用
- Setter 注入:可选依赖,可变,适合可选依赖
9. @Autowired 注解的工作原理?
答案:
@Autowired
是 Spring 的自动装配注解,通过以下方式工作:
- 按类型匹配 (byType)
- 按名称匹配 (byName)
- 构造器注入
- 字段注入
匹配优先级:
- 按类型找到唯一 Bean
- 按类型找到多个 Bean,再按名称匹配
- 使用
@Qualifier
指定 Bean 名称 - 使用
@Primary
标记主要候选者
AOP 面向切面编程
10. 什么是 AOP?深入理解面向切面编程
答案:
面试2分钟表述:
AOP(面向切面编程)是一种编程范式,核心思想是将横切关注点从业务逻辑中分离出来。
核心概念:
- 切面:横切关注点的模块化实现,如日志、事务、安全
- 切点:定义哪些方法需要被拦截
- 通知:在切点执行的代码,有5种类型:前置、后置、返回、异常、环绕
实际应用:
@Aspect
@Component
public class LoggingAspect {@Around("execution(* com.example.service.*.*(..))")public Object logMethod(ProceedingJoinPoint joinPoint) throws Throwable {long start = System.currentTimeMillis();Object result = joinPoint.proceed();long end = System.currentTimeMillis();System.out.println("方法耗时: " + (end - start) + "ms");return result;}
}
AOP的优势:
- 关注点分离:业务逻辑只关注核心功能
- 代码复用:一个切面可以应用到多个方法
- 易于维护:横切关注点的修改只需要修改切面
- 灵活性:可以动态启用/禁用切面
Spring AOP实现:
- 使用JDK动态代理(基于接口)或CGLIB代理(基于继承)
- 通过代理对象拦截方法调用,在目标方法执行前后插入切面逻辑
详细技术说明:
AOP 核心思想:
AOP (Aspect-Oriented Programming) 是一种编程范式,用于将横切关注点(Cross-cutting Concerns)从业务逻辑中分离出来,实现关注点的分离和模块化。
为什么需要 AOP?
传统编程的问题:
// 传统方式:业务逻辑和横切关注点混合
public class UserService {public void saveUser(User user) {// 日志记录System.out.println("开始保存用户: " + user.getName());try {// 业务逻辑userRepository.save(user);// 日志记录System.out.println("用户保存成功: " + user.getName());} catch (Exception e) {// 异常处理System.out.println("用户保存失败: " + e.getMessage());throw e;}}public void deleteUser(Long id) {// 重复的日志记录代码System.out.println("开始删除用户: " + id);try {// 业务逻辑userRepository.deleteById(id);// 重复的日志记录代码System.out.println("用户删除成功: " + id);} catch (Exception e) {// 重复的异常处理代码System.out.println("用户删除失败: " + e.getMessage());throw e;}}
}
AOP 解决方案:
// 业务逻辑:只关注核心功能
@Service
public class UserService {public void saveUser(User user) {userRepository.save(user); // 纯业务逻辑}public void deleteUser(Long id) {userRepository.deleteById(id); // 纯业务逻辑}
}// 切面:专门处理横切关注点
@Aspect
@Component
public class LoggingAspect {@Before("execution(* com.example.service.*.*(..))")public void beforeMethod(JoinPoint joinPoint) {System.out.println("方法执行前: " + joinPoint.getSignature());}@AfterReturning("execution(* com.example.service.*.*(..))")public void afterReturning(JoinPoint joinPoint) {System.out.println("方法执行成功: " + joinPoint.getSignature());}@AfterThrowing("execution(* com.example.service.*.*(..))")public void afterThrowing(JoinPoint joinPoint, Throwable error) {System.out.println("方法执行失败: " + error.getMessage());}
}
AOP 核心概念详解:
1. 切面 (Aspect)
@Aspect
@Component
public class LoggingAspect {// 切面是横切关注点的模块化实现// 包含多个通知和切点
}
2. 连接点 (Join Point)
// 连接点是程序执行的特定点,如方法调用、异常抛出等
public class UserService {public void saveUser(User user) { // 这是一个连接点userRepository.save(user);}
}
3. 切点 (Pointcut)
@Aspect
public class LoggingAspect {// 切点是连接点的集合,定义哪些连接点需要被拦截@Pointcut("execution(* com.example.service.*.*(..))")public void serviceMethods() {}@Before("serviceMethods()")public void beforeMethod(JoinPoint joinPoint) {// 通知逻辑}
}
4. 通知 (Advice)
@Aspect
public class LoggingAspect {// 前置通知:在目标方法执行前执行@Before("execution(* com.example.service.*.*(..))")public void beforeMethod(JoinPoint joinPoint) {System.out.println("方法执行前");}// 后置通知:在目标方法执行后执行(无论成功还是失败)@After("execution(* com.example.service.*.*(..))")public void afterMethod(JoinPoint joinPoint) {System.out.println("方法执行后");}// 返回通知:在目标方法成功返回后执行@AfterReturning("execution(* com.example.service.*.*(..))")public void afterReturning(JoinPoint joinPoint) {System.out.println("方法执行成功");}// 异常通知:在目标方法抛出异常后执行@AfterThrowing("execution(* com.example.service.*.*(..))")public void afterThrowing(JoinPoint joinPoint, Throwable error) {System.out.println("方法执行失败: " + error.getMessage());}// 环绕通知:包围目标方法,可以控制方法的执行@Around("execution(* com.example.service.*.*(..))")public Object aroundMethod(ProceedingJoinPoint joinPoint) throws Throwable {System.out.println("方法执行前");Object result = joinPoint.proceed(); // 执行目标方法System.out.println("方法执行后");return result;}
}
5. 目标对象 (Target)
// 目标对象是被代理的原始对象
@Service
public class UserService { // 这是目标对象public void saveUser(User user) {userRepository.save(user);}
}
6. 代理 (Proxy)
// 代理是AOP框架创建的对象,用于拦截对目标对象的调用
// Spring会为UserService创建一个代理对象
// 当调用userService.saveUser()时,实际上是调用代理对象的方法
AOP 的优势:
1. 关注点分离
- 业务逻辑只关注核心功能
- 横切关注点(日志、事务、安全)独立处理
2. 代码复用
- 一个切面可以应用到多个类和方法
- 避免重复代码
3. 易于维护
- 横切关注点的修改只需要修改切面
- 业务逻辑和横切关注点独立演化
4. 灵活性
- 可以通过配置动态启用/禁用切面
- 支持多种通知类型
实际应用场景:
1. 日志记录
@Aspect
@Component
public class LoggingAspect {@Around("execution(* com.example.service.*.*(..))")public Object logMethod(ProceedingJoinPoint joinPoint) throws Throwable {long start = System.currentTimeMillis();Object result = joinPoint.proceed();long end = System.currentTimeMillis();System.out.println("方法: " + joinPoint.getSignature() + ", 耗时: " + (end - start) + "ms");return result;}
}
2. 事务管理
@Aspect
@Component
public class TransactionAspect {@Around("@annotation(Transactional)")public Object manageTransaction(ProceedingJoinPoint joinPoint) throws Throwable {// 开启事务TransactionManager.begin();try {Object result = joinPoint.proceed();// 提交事务TransactionManager.commit();return result;} catch (Exception e) {// 回滚事务TransactionManager.rollback();throw e;}}
}
3. 权限控制
@Aspect
@Component
public class SecurityAspect {@Before("@annotation(RequirePermission)")public void checkPermission(JoinPoint joinPoint) {RequirePermission annotation = getAnnotation(joinPoint);if (!hasPermission(annotation.value())) {throw new SecurityException("权限不足");}}
}
AOP 的局限性:
- 只能拦截public方法
- 不能拦截final方法
- 不能拦截static方法
- 同类内部调用不会触发切面
11. Spring AOP 的通知类型有哪些?
答案:
- @Before: 前置通知
- @After: 后置通知
- @AfterReturning: 返回通知
- @AfterThrowing: 异常通知
- @Around: 环绕通知
示例:
@Aspect
@Component
public class LoggingAspect {@Before("execution(* com.example.service.*.*(..))")public void beforeMethod(JoinPoint joinPoint) {System.out.println("方法执行前: " + joinPoint.getSignature());}@Around("execution(* com.example.service.*.*(..))")public Object aroundMethod(ProceedingJoinPoint joinPoint) throws Throwable {System.out.println("方法执行前");Object result = joinPoint.proceed();System.out.println("方法执行后");return result;}
}
12. Spring AOP 的代理机制详解?
答案:
Spring AOP 使用两种代理机制来实现面向切面编程:
JDK 动态代理:
原理:基于接口的代理,运行时动态生成代理类
// 目标接口
public interface UserService {void saveUser(User user);void deleteUser(Long id);
}// 目标类
public class UserServiceImpl implements UserService {@Overridepublic void saveUser(User user) {System.out.println("保存用户: " + user.getName());}@Overridepublic void deleteUser(Long id) {System.out.println("删除用户: " + id);}
}// JDK动态代理实现
public class JdkProxyExample {public static void main(String[] args) {UserService target = new UserServiceImpl();UserService proxy = (UserService) Proxy.newProxyInstance(target.getClass().getClassLoader(), // 类加载器target.getClass().getInterfaces(), // 接口数组new InvocationHandler() { // 调用处理器@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("方法执行前: " + method.getName());Object result = method.invoke(target, args);System.out.println("方法执行后: " + method.getName());return result;}});proxy.saveUser(new User("张三"));}
}
CGLIB 代理:
原理:基于继承的代理,运行时生成目标类的子类
// 目标类(不需要实现接口)
public class UserService {public void saveUser(User user) {System.out.println("保存用户: " + user.getName());}public void deleteUser(Long id) {System.out.println("删除用户: " + id);}
}// CGLIB代理实现
public class CglibProxyExample {public static void main(String[] args) {Enhancer enhancer = new Enhancer();enhancer.setSuperclass(UserService.class); // 设置父类enhancer.setCallback(new MethodInterceptor() {@Overridepublic Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {System.out.println("方法执行前: " + method.getName());Object result = proxy.invokeSuper(obj, args); // 调用父类方法System.out.println("方法执行后: " + method.getName());return result;}});UserService proxy = (UserService) enhancer.create();proxy.saveUser(new User("李四"));}
}
两种代理的详细对比:
特性 | JDK动态代理 | CGLIB代理 |
---|---|---|
实现方式 | 基于接口 | 基于继承 |
目标要求 | 必须实现接口 | 不需要接口 |
代理对象 | 实现相同接口 | 继承目标类 |
方法调用 | 通过反射调用 | 直接调用父类方法 |
性能 | 反射调用较慢 | 直接调用较快 |
限制 | 只能代理接口方法 | 不能代理final方法 |
依赖 | JDK自带 | 需要CGLIB库 |
Spring中的选择规则:
// Spring配置
@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true) // 强制使用CGLIB
public class AopConfig {// ...
}// 或者通过XML配置
<aop:aspectj-autoproxy proxy-target-class="true"/>
实际应用示例:
// 有接口的情况 - 使用JDK动态代理
@Service
public class UserServiceImpl implements UserService {@Overridepublic void saveUser(User user) {// 业务逻辑}
}// 无接口的情况 - 使用CGLIB代理
@Service
public class UserService {public void saveUser(User user) {// 业务逻辑}
}// 切面类
@Aspect
@Component
public class LoggingAspect {@Before("execution(* com.example.service.*.*(..))")public void beforeMethod(JoinPoint joinPoint) {System.out.println("方法执行前: " + joinPoint.getSignature());}
}
性能对比:
// JDK动态代理性能测试
public class ProxyPerformanceTest {public static void main(String[] args) {// JDK动态代理long start = System.currentTimeMillis();for (int i = 0; i < 1000000; i++) {// 调用代理方法}long jdkTime = System.currentTimeMillis() - start;// CGLIB代理start = System.currentTimeMillis();for (int i = 0; i < 1000000; i++) {// 调用代理方法}long cglibTime = System.currentTimeMillis() - start;System.out.println("JDK代理耗时: " + jdkTime + "ms");System.out.println("CGLIB代理耗时: " + cglibTime + "ms");}
}
选择建议:
- 有接口:优先使用JDK动态代理,符合面向接口编程
- 无接口:使用CGLIB代理,性能更好
- 性能敏感:考虑使用CGLIB代理
- 需要代理final方法:只能使用JDK动态代理(但final方法本身不能被代理)
Spring MVC
13. Spring MVC 的工作流程?
答案:
- 用户请求 → DispatcherServlet
- DispatcherServlet → HandlerMapping 查找处理器
- HandlerMapping → 返回 HandlerExecutionChain
- DispatcherServlet → HandlerAdapter 调用处理器
- HandlerAdapter → 调用 Controller 方法
- Controller → 返回 ModelAndView
- DispatcherServlet → ViewResolver 解析视图
- ViewResolver → 返回 View 对象
- DispatcherServlet → 渲染视图
- 响应 → 返回给用户
14. @Controller 和 @RestController 的区别?
答案:
@Controller:
- 返回视图名称
- 需要配合
@ResponseBody
返回 JSON - 用于传统 Web 应用
@RestController:
- 组合注解:
@Controller
+@ResponseBody
- 直接返回数据(JSON/XML)
- 用于 RESTful API
示例:
@Controller
public class UserController {@RequestMapping("/user")public String getUser() {return "user"; // 返回视图名称}@RequestMapping("/user/json")@ResponseBodypublic User getUserJson() {return new User(); // 返回 JSON}
}@RestController
public class UserRestController {@RequestMapping("/user")public User getUser() {return new User(); // 直接返回 JSON}
}
15. Spring MVC 的常用注解?
答案:
请求映射:
@RequestMapping
: 通用请求映射@GetMapping
: GET 请求映射@PostMapping
: POST 请求映射@PutMapping
: PUT 请求映射@DeleteMapping
: DELETE 请求映射
参数绑定:
@RequestParam
: 绑定请求参数@PathVariable
: 绑定路径变量@RequestBody
: 绑定请求体@ModelAttribute
: 绑定模型属性
示例:
@RestController
@RequestMapping("/api/users")
public class UserController {@GetMapping("/{id}")public User getUser(@PathVariable Long id) {return userService.findById(id);}@PostMappingpublic User createUser(@RequestBody User user) {return userService.save(user);}@GetMappingpublic List<User> getUsers(@RequestParam(defaultValue = "0") int page) {return userService.findAll(page);}
}
Spring Boot
16. 什么是 Spring Boot?
答案:
Spring Boot 是基于 Spring 框架的快速开发脚手架,简化了 Spring 应用的配置和部署。
核心特性:
- 自动配置: 根据依赖自动配置 Bean
- 起步依赖: 预定义的依赖组合
- 内嵌服务器: 无需外部容器
- 生产就绪: 监控、健康检查等特性
17. @SpringBootApplication 组合注解详解?
答案:
@SpringBootApplication
是一个组合注解,包含三个核心注解:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration // 1. 配置类标识
@EnableAutoConfiguration // 2. 启用自动配置
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {// ...
}
三个核心注解的作用:
-
@SpringBootConfiguration:
- 等价于
@Configuration
- 标识这是一个配置类
- 允许在上下文中注册额外的Bean
- 等价于
-
@EnableAutoConfiguration:
- 启用Spring Boot的自动配置机制
- 根据类路径依赖自动配置Bean
- 核心功能:自动配置
-
@ComponentScan:
- 启用组件扫描
- 扫描当前包及子包下的组件
- 自动注册
@Component
、@Service
、@Repository
等注解的类
等价写法:
// 使用组合注解
@SpringBootApplication
public class Application {public static void main(String[] args) {SpringApplication.run(Application.class, args);}
}// 等价于分别使用三个注解
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan
public class Application {public static void main(String[] args) {SpringApplication.run(Application.class, args);}
}
18. Spring Boot 启动与自动装配完整流程(源码级深度解析)
答案: Spring Boot 启动时的自动装配是整个框架的核心机制,通过一系列精妙的设计实现了"约定优于配置"。
重要说明:以下源码是基于 Spring Boot 2.x/3.x 真实源码简化而来,保留核心逻辑,去除了异常处理、日志、部分参数校验等非核心代码,便于面试理解和讲解。实际源码会更复杂,包含更多的边界处理和优化。
Spring Boot启动时的自动装配流程分为5个阶段:首先,SpringApplication.run()启动容器,创建ApplicationContext。第二,通过@SpringBootApplication中的@EnableAutoConfiguration触发自动配置。第三,AutoConfigurationImportSelector从META-INF/spring.factories加载所有候选配置类。第四,通过@Conditional系列注解过滤,只保留满足条件的配置类。第五,Spring容器解析配置类,创建Bean并完成依赖注入。整个过程支持自定义排序、延迟加载和优先级控制。
阶段1:SpringApplication启动
核心入口:SpringApplication.run()
public class SpringApplication {public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {return run(new Class<?>[] { primarySource }, args);}public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {return new SpringApplication(primarySources).run(args);}// 核心run方法public ConfigurableApplicationContext run(String... args) {// 1. 创建StopWatch,记录启动时间StopWatch stopWatch = new StopWatch();stopWatch.start();// 2. 创建引导上下文DefaultBootstrapContext bootstrapContext = createBootstrapContext();ConfigurableApplicationContext context = null;// 3. 配置headless属性configureHeadlessProperty();// 4. 获取SpringApplicationRunListeners并启动SpringApplicationRunListeners listeners = getRunListeners(args);listeners.starting(bootstrapContext, this.mainApplicationClass);try {// 5. 封装命令行参数ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);// 6. 准备环境ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);configureIgnoreBeanInfo(environment);// 7. 打印BannerBanner printedBanner = printBanner(environment);// 8. 创建ApplicationContext(重点)context = createApplicationContext();context.setApplicationStartup(this.applicationStartup);// 9. 准备上下文(自动装配的入口)prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);// 10. 刷新上下文(Bean的创建和初始化)refreshContext(context);// 11. 刷新后的处理afterRefresh(context, applicationArguments);stopWatch.stop();// 12. 发布启动完成事件listeners.started(context);// 13. 调用RunnerscallRunners(context, applicationArguments);} catch (Throwable ex) {handleRunFailure(context, ex, listeners);throw new IllegalStateException(ex);}try {listeners.running(context);} catch (Throwable ex) {handleRunFailure(context, ex, null);throw new IllegalStateException(ex);}return context;}
}
阶段2:准备上下文(自动装配入口)
核心方法:prepareContext()
private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,ApplicationArguments applicationArguments, Banner printedBanner) {// 1. 设置环境context.setEnvironment(environment);// 2. 后置处理ApplicationContextpostProcessApplicationContext(context);// 3. 应用所有的ApplicationContextInitializerapplyInitializers(context);// 4. 发布上下文准备完成事件listeners.contextPrepared(context);// 5. 注册启动参数bootstrapContext.close(context);// 6. 打印启动信息if (this.logStartupInfo) {logStartupInfo(context.getParent() == null);logStartupProfileInfo(context);}// 7. 注册单例BeanConfigurableListableBeanFactory beanFactory = context.getBeanFactory();beanFactory.registerSingleton("springApplicationArguments", applicationArguments);if (printedBanner != null) {beanFactory.registerSingleton("springBootBanner", printedBanner);}// 8. 设置是否允许Bean定义覆盖if (beanFactory instanceof DefaultListableBeanFactory) {((DefaultListableBeanFactory) beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);}// 9. 加载LazyInitializationif (this.lazyInitialization) {context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());}// 10. 获取所有的sources(包含主配置类)Set<Object> sources = getAllSources();Assert.notEmpty(sources, "Sources must not be empty");// 11. 加载Bean定义(关键:这里会触发@EnableAutoConfiguration)load(context, sources.toArray(new Object[0]));// 12. 发布上下文加载完成事件listeners.contextLoaded(context);
}
阶段3:加载Bean定义
核心方法:load()
protected void load(ApplicationContext context, Object[] sources) {BeanDefinitionLoader loader = createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources);if (this.beanNameGenerator != null) {loader.setBeanNameGenerator(this.beanNameGenerator);}if (this.resourceLoader != null) {loader.setResourceLoader(this.resourceLoader);}if (this.environment != null) {loader.setEnvironment(this.environment);}// 执行加载loader.load();
}// BeanDefinitionLoader.load()
private int load(Object source) {Assert.notNull(source, "Source must not be null");// 1. 如果是Class类型,使用AnnotatedBeanDefinitionReader加载if (source instanceof Class<?>) {return load((Class<?>) source);}// 2. 如果是Resource类型,使用XmlBeanDefinitionReader加载if (source instanceof Resource) {return load((Resource) source);}// 3. 如果是Package类型,使用ClassPathBeanDefinitionScanner扫描if (source instanceof Package) {return load((Package) source);}// 4. 如果是CharSequence类型,尝试加载类或资源if (source instanceof CharSequence) {return load((CharSequence) source);}throw new IllegalArgumentException("Invalid source type " + source.getClass());
}
阶段4:处理@EnableAutoConfiguration
核心类:ConfigurationClassParser
public class ConfigurationClassParser {public void parse(Set<BeanDefinitionHolder> configCandidates) {for (BeanDefinitionHolder holder : configCandidates) {BeanDefinition bd = holder.getBeanDefinition();try {if (bd instanceof AnnotatedBeanDefinition) {// 解析注解配置类(主配置类会在这里被解析)parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());}} catch (BeanDefinitionStoreException ex) {throw ex;}}// 处理延迟导入的配置(DeferredImportSelector)this.deferredImportSelectorHandler.process();}protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {// 1. 处理@Conditional注解if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {return;}// 2. 处理@PropertySource注解processPropertySource(configClass);// 3. 处理@ComponentScan注解Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);// 4. 处理@Import注解(关键:这里会处理AutoConfigurationImportSelector)processImports(configClass, sourceClass, getImports(sourceClass), true);// 5. 处理@ImportResource注解processImportResource(configClass);// 6. 处理@Bean方法processBeanMethods(configClass);}
}
阶段5:AutoConfigurationImportSelector工作
核心类:AutoConfigurationImportSelector
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {@Overridepublic String[] selectImports(AnnotationMetadata annotationMetadata) {// 1. 检查自动配置是否启用if (!isEnabled(annotationMetadata)) {return NO_IMPORTS;}// 2. 获取自动配置条目AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);// 3. 返回要导入的配置类名称数组return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());}protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {if (!isEnabled(annotationMetadata)) {return EMPTY_ENTRY;}// 1. 获取@EnableAutoConfiguration注解的属性AnnotationAttributes attributes = getAttributes(annotationMetadata);// 2. 获取候选配置类(从spring.factories加载)List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);// 3. 去除重复的配置类configurations = removeDuplicates(configurations);// 4. 获取需要排除的配置类Set<String> exclusions = getExclusions(annotationMetadata, attributes);checkExcludedClasses(configurations, exclusions);configurations.removeAll(exclusions);// 5. 通过过滤器过滤配置类(关键:条件注解在这里生效)configurations = getConfigurationClassFilter().filter(configurations);// 6. 触发自动配置导入事件fireAutoConfigurationImportEvents(configurations, exclusions);return new AutoConfigurationEntry(configurations, exclusions);}protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {// 从META-INF/spring.factories加载所有自动配置类List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. " +"If you are using a custom packaging, make sure that file is correct.");return configurations;}
}
阶段6:SpringFactoriesLoader加载配置
核心类:SpringFactoriesLoader
public final class SpringFactoriesLoader {public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {String factoryTypeName = factoryType.getName();return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());}private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {// 1. 尝试从缓存获取MultiValueMap<String, String> result = cache.get(classLoader);if (result != null) {return result;}try {// 2. 加载所有的META-INF/spring.factories文件Enumeration<URL> urls = (classLoader != null ?classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));result = new LinkedMultiValueMap<>();// 3. 遍历所有的spring.factories文件while (urls.hasMoreElements()) {URL url = urls.nextElement();UrlResource resource = new UrlResource(url);// 4. 加载PropertiesProperties properties = PropertiesLoaderUtils.loadProperties(resource);// 5. 解析配置for (Map.Entry<?, ?> entry : properties.entrySet()) {String factoryTypeName = ((String) entry.getKey()).trim();// 6. 将配置类名称按逗号分割并添加到结果中for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {result.add(factoryTypeName, factoryImplementationName.trim());}}}// 7. 放入缓存cache.put(classLoader, result);return result;} catch (IOException ex) {throw new IllegalArgumentException("Unable to load factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex);}}
}
spring.factories文件格式:
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration
# ... 更多配置类
阶段7:条件注解过滤
核心类:OnClassCondition
@Order(Ordered.HIGHEST_PRECEDENCE)
class OnClassCondition extends FilteringSpringBootCondition {@Overridepublic ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {ClassLoader classLoader = context.getClassLoader();ConditionMessage matchMessage = ConditionMessage.empty();// 1. 获取@ConditionalOnClass注解的valueList<String> onClasses = getCandidates(metadata, ConditionalOnClass.class);if (onClasses != null) {// 2. 检查类是否存在List<String> missing = filter(onClasses, ClassNameFilter.MISSING, classLoader);// 3. 如果有缺失的类,返回不匹配if (!missing.isEmpty()) {return ConditionOutcome.noMatch(ConditionMessage.forCondition(ConditionalOnClass.class).didNotFind("required class", "required classes").items(Style.QUOTE, missing));}matchMessage = matchMessage.andCondition(ConditionalOnClass.class).found("required class", "required classes").items(Style.QUOTE, filter(onClasses, ClassNameFilter.PRESENT, classLoader));}// 4. 获取@ConditionalOnMissingClass注解的valueList<String> onMissingClasses = getCandidates(metadata, ConditionalOnMissingClass.class);if (onMissingClasses != null) {// 5. 检查类是否不存在List<String> present = filter(onMissingClasses, ClassNameFilter.PRESENT, classLoader);// 6. 如果有存在的类,返回不匹配if (!present.isEmpty()) {return ConditionOutcome.noMatch(ConditionMessage.forCondition(ConditionalOnMissingClass.class).found("unwanted class", "unwanted classes").items(Style.QUOTE, present));}matchMessage = matchMessage.andCondition(ConditionalOnMissingClass.class).didNotFind("unwanted class", "unwanted classes").items(Style.QUOTE, filter(onMissingClasses, ClassNameFilter.MISSING, classLoader));}return ConditionOutcome.match(matchMessage);}
}
条件注解示例:
@Configuration
@ConditionalOnClass(DataSource.class) // 类路径存在DataSource类
@ConditionalOnMissingBean(DataSource.class) // 容器中不存在DataSource Bean
@EnableConfigurationProperties(DataSourceProperties.class)
public class DataSourceAutoConfiguration {@Bean@ConditionalOnProperty(prefix = "spring.datasource", name = "url") // 配置文件存在指定属性public DataSource dataSource(DataSourceProperties properties) {return DataSourceBuilder.create().url(properties.getUrl()).username(properties.getUsername()).password(properties.getPassword()).build();}
}
阶段8:配置类排序
核心接口:AutoConfigurationSorter
class AutoConfigurationSorter {List<String> getInPriorityOrder(Collection<String> classNames) {// 1. 构建自动配置类的图结构AutoConfigurationClasses classes = new AutoConfigurationClasses(this.metadataReaderFactory, this.autoConfigurationMetadata, classNames);List<String> orderedClassNames = new ArrayList<>(classNames);// 2. 按@AutoConfigureOrder排序orderedClassNames.sort((o1, o2) -> {int i1 = classes.get(o1).getOrder();int i2 = classes.get(o2).getOrder();return Integer.compare(i1, i2);});// 3. 处理@AutoConfigureBefore和@AutoConfigureAfterorderedClassNames = sortByAnnotation(classes, orderedClassNames);return orderedClassNames;}private List<String> sortByAnnotation(AutoConfigurationClasses classes, List<String> classNames) {List<String> result = new ArrayList<>(classNames.size());Set<String> seen = new HashSet<>();for (String className : classNames) {doSortByAfterAnnotation(classes, className, result, seen, null);}return result;}
}
排序注解示例:
// WebMvcAutoConfiguration 在 DispatcherServletAutoConfiguration 之后配置
@Configuration
@AutoConfigureAfter(DispatcherServletAutoConfiguration.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
public class WebMvcAutoConfiguration {// ...
}// DataSourceAutoConfiguration 在 HibernateJpaAutoConfiguration 之前配置
@Configuration
@AutoConfigureBefore(HibernateJpaAutoConfiguration.class)
@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })
public class DataSourceAutoConfiguration {// ...
}
完整流程时序图
用户代码 SpringApplication ConfigurationClassParser AutoConfigurationImportSelector| | | ||--run()------------------> | | || |--createApplicationContext()->| || | | || |--prepareContext()---------->| || | | || | |--parse()----------------------->|| | | || | | |--selectImports()| | | || | | |--getCandidateConfigurations()| | | | (加载spring.factories)| | | || | | |--filter()| | | | (条件注解过滤)| | | || | |<--(返回配置类列表)-------------|| | | || | |--registerBeanDefinitions() || | | || |--refreshContext()---------->| || | (创建Bean实例) | || | | ||<--(返回ApplicationContext)| | |
源码流程总结:
- 启动阶段:SpringApplication.run() 创建并准备 ApplicationContext
- 加载阶段:通过 BeanDefinitionLoader 加载主配置类的 Bean 定义
- 解析阶段:ConfigurationClassParser 解析 @Import 注解,触发 AutoConfigurationImportSelector
- 选择阶段:从 spring.factories 加载所有候选自动配置类
- 过滤阶段:通过 @Conditional 系列注解过滤,只保留满足条件的配置类
- 排序阶段:根据 @AutoConfigureOrder、@AutoConfigureBefore、@AutoConfigureAfter 排序
- 注册阶段:将过滤后的配置类注册为 BeanDefinition
- 刷新阶段:refreshContext() 创建所有单例 Bean 实例
补充:@EnableAutoConfiguration 执行入口与生效机制
一、执行入口详解
核心问题:@EnableAutoConfiguration 注解是如何被触发执行的?
执行入口调用链:
SpringApplication.run()↓
AbstractApplicationContext.refresh()↓
invokeBeanFactoryPostProcessors()↓
PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors()↓
ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry()↓
ConfigurationClassPostProcessor.processConfigBeanDefinitions()↓
ConfigurationClassParser.parse()↓
ConfigurationClassParser.doProcessConfigurationClass()↓
ConfigurationClassParser.processImports()↓
DeferredImportSelectorHandler.process()↓
AutoConfigurationImportSelector.selectImports()
详细源码执行流程
入口1:AbstractApplicationContext.refresh()
@Override
public void refresh() throws BeansException, IllegalStateException {synchronized (this.startupShutdownMonitor) {// 1. 准备刷新上下文prepareRefresh();// 2. 获取BeanFactoryConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();// 3. 准备BeanFactoryprepareBeanFactory(beanFactory);try {// 4. 后置处理BeanFactorypostProcessBeanFactory(beanFactory);// 5. 调用BeanFactoryPostProcessor(关键入口)// @EnableAutoConfiguration 在这里开始执行invokeBeanFactoryPostProcessors(beanFactory);// 6. 注册BeanPostProcessorregisterBeanPostProcessors(beanFactory);// 7. 初始化MessageSourceinitMessageSource();// 8. 初始化事件广播器initApplicationEventMulticaster();// 9. 刷新特定上下文的子类onRefresh();// 10. 注册监听器registerListeners();// 11. 实例化所有非懒加载的单例BeanfinishBeanFactoryInitialization(beanFactory);// 12. 完成刷新finishRefresh();} catch (BeansException ex) {destroyBeans();cancelRefresh(ex);throw ex;} finally {resetCommonCaches();}}
}
入口2:invokeBeanFactoryPostProcessors()
protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {// 委托给PostProcessorRegistrationDelegate处理PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
}
入口3:PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors()
public static void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {// 1. 首先处理BeanDefinitionRegistryPostProcessorSet<String> processedBeans = new HashSet<>();if (beanFactory instanceof BeanDefinitionRegistry) {BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;List<BeanFactoryPostProcessor> regularPostProcessors = new ArrayList<>();List<BeanDefinitionRegistryPostProcessor> registryProcessors = new ArrayList<>();// 2. 分类处理for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {BeanDefinitionRegistryPostProcessor registryProcessor =(BeanDefinitionRegistryPostProcessor) postProcessor;registryProcessor.postProcessBeanDefinitionRegistry(registry);registryProcessors.add(registryProcessor);} else {regularPostProcessors.add(postProcessor);}}// 3. 获取所有的BeanDefinitionRegistryPostProcessorString[] postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);// 4. 按优先级排序并执行// ConfigurationClassPostProcessor 在这里被执行(关键!)List<BeanDefinitionRegistryPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();for (String ppName : postProcessorNames) {if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {priorityOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));processedBeans.add(ppName);}}sortPostProcessors(priorityOrderedPostProcessors, beanFactory);registryProcessors.addAll(priorityOrderedPostProcessors);// 5. 调用postProcessBeanDefinitionRegistry方法invokeBeanDefinitionRegistryPostProcessors(priorityOrderedPostProcessors, registry);}
}
入口4:ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry()
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {int registryId = System.identityHashCode(registry);if (this.registriesPostProcessed.contains(registryId)) {throw new IllegalStateException("postProcessBeanDefinitionRegistry already called on this post-processor against " + registry);}if (this.factoriesPostProcessed.contains(registryId)) {throw new IllegalStateException("postProcessBeanFactory already called on this post-processor against " + registry);}this.registriesPostProcessed.add(registryId);// 处理配置类Bean定义(关键调用)processConfigBeanDefinitions(registry);
}
入口5:ConfigurationClassPostProcessor.processConfigBeanDefinitions()
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {List<BeanDefinitionHolder> configCandidates = new ArrayList<>();String[] candidateNames = registry.getBeanDefinitionNames();// 1. 找出所有的配置类候选者for (String beanName : candidateNames) {BeanDefinition beanDef = registry.getBeanDefinition(beanName);if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) {// 已经处理过} else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {// 是配置类,添加到候选列表configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));}}// 2. 如果没有找到配置类,直接返回if (configCandidates.isEmpty()) {return;}// 3. 按@Order排序configCandidates.sort((bd1, bd2) -> {int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());return Integer.compare(i1, i2);});// 4. 创建ConfigurationClassParser解析器ConfigurationClassParser parser = new ConfigurationClassParser(this.metadataReaderFactory, this.problemReporter, this.environment,this.resourceLoader, this.componentScanBeanNameGenerator, registry);Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());// 5. 循环解析所有配置类do {// 解析配置类(关键:这里会触发@EnableAutoConfiguration)parser.parse(candidates);parser.validate();Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());configClasses.removeAll(alreadyParsed);// 6. 读取配置类并注册BeanDefinitionif (this.reader == null) {this.reader = new ConfigurationClassBeanDefinitionReader(registry, this.sourceExtractor, this.resourceLoader, this.environment,this.importBeanNameGenerator, parser.getImportRegistry());}this.reader.loadBeanDefinitions(configClasses);alreadyParsed.addAll(configClasses);candidates.clear();// 检查是否有新的候选配置类if (registry.getBeanDefinitionCount() > candidateNames.length) {String[] newCandidateNames = registry.getBeanDefinitionNames();Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames));Set<String> alreadyParsedClasses = new HashSet<>();for (ConfigurationClass configurationClass : alreadyParsed) {alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());}for (String candidateName : newCandidateNames) {if (!oldCandidateNames.contains(candidateName)) {BeanDefinition bd = registry.getBeanDefinition(candidateName);if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) &&!alreadyParsedClasses.contains(bd.getBeanClassName())) {candidates.add(new BeanDefinitionHolder(bd, candidateName));}}}candidateNames = newCandidateNames;}} while (!candidates.isEmpty());
}
入口6:ConfigurationClassParser.parse()
public void parse(Set<BeanDefinitionHolder> configCandidates) {for (BeanDefinitionHolder holder : configCandidates) {BeanDefinition bd = holder.getBeanDefinition();try {if (bd instanceof AnnotatedBeanDefinition) {// 解析注解配置类(主配置类在这里)parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());} else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());} else {parse(bd.getBeanClassName(), holder.getBeanName());}} catch (BeanDefinitionStoreException ex) {throw ex;}}// 处理延迟导入选择器(关键:AutoConfigurationImportSelector在这里执行)this.deferredImportSelectorHandler.process();
}protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException {processConfigurationClass(new ConfigurationClass(metadata, beanName));
}
入口7:ConfigurationClassParser.doProcessConfigurationClass()
上层调用链:
ConfigurationClassParser.parse(Set<BeanDefinitionHolder> configCandidates)↓
ConfigurationClassParser.parse(AnnotationMetadata metadata, String beanName)↓
ConfigurationClassParser.processConfigurationClass(ConfigurationClass configClass)↓
ConfigurationClassParser.doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass) ← 当前方法
processConfigurationClass方法(直接上层调用):
protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {// 1. 判断是否需要跳过(@Conditional条件判断)if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {return;}// 2. 检查是否已经处理过该配置类ConfigurationClass existingClass = this.configurationClasses.get(configClass);if (existingClass != null) {if (configClass.isImported()) {if (existingClass.isImported()) {existingClass.mergeImportedBy(configClass);}return;} else {this.configurationClasses.remove(configClass);}}// 3. 处理配置类(调用doProcessConfigurationClass)SourceClass sourceClass = asSourceClass(configClass, DEFAULT_EXCLUSION_FILTER);do {sourceClass = doProcessConfigurationClass(configClass, sourceClass);} while (sourceClass != null);// 4. 将配置类添加到已处理集合this.configurationClasses.put(configClass, configClass);
}
核心方法实现:
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass) throws IOException {// 1. 处理@Component注解if (configClass.getMetadata().isAnnotated(Component.class.getName())) {processMemberClasses(configClass, sourceClass);}// 2. 处理@PropertySources注解for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(sourceClass.getMetadata(), PropertySources.class, PropertySource.class)) {if (this.environment instanceof ConfigurableEnvironment) {processPropertySource(propertySource);}}// 3. 处理@ComponentScan注解Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);if (!componentScans.isEmpty()) {for (AnnotationAttributes componentScan : componentScans) {Set<BeanDefinitionHolder> scannedBeanDefinitions = this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());for (BeanDefinitionHolder holder : scannedBeanDefinitions) {parse(holder.getBeanDefinition().getBeanClassName(), holder.getBeanName());}}}// 4. 处理@Import注解(关键:这里会处理@EnableAutoConfiguration中的@Import)processImports(configClass, sourceClass, getImports(sourceClass), true);// 5. 处理@ImportResource注解AnnotationAttributes importResource = AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);if (importResource != null) {String[] resources = importResource.getStringArray("locations");Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");for (String resource : resources) {String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);configClass.addImportedResource(resolvedResource, readerClass);}}// 6. 处理@Bean方法Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);for (MethodMetadata methodMetadata : beanMethods) {configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));}return null;
}
入口8:ConfigurationClassParser.processImports()
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,Collection<SourceClass> importCandidates, boolean checkForCircularImports) {if (importCandidates.isEmpty()) {return;}// 循环导入检查if (checkForCircularImports && isChainedImportOnStack(configClass)) {this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));} else {this.importStack.push(configClass);try {for (SourceClass candidate : importCandidates) {// 1. 如果是ImportSelector(AutoConfigurationImportSelector是其子接口)if (candidate.isAssignable(ImportSelector.class)) {Class<?> candidateClass = candidate.loadClass();ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class, this.environment, this.resourceLoader, this.registry);// 2. 如果是DeferredImportSelector(AutoConfigurationImportSelector实现了这个接口)if (selector instanceof DeferredImportSelector) {// 添加到延迟处理器(关键:AutoConfigurationImportSelector在这里被添加)this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);} else {// 普通ImportSelector立即执行String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);processImports(configClass, currentSourceClass, importSourceClasses, false);}} else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {// 3. 如果是ImportBeanDefinitionRegistrarClass<?> candidateClass = candidate.loadClass();ImportBeanDefinitionRegistrar registrar = ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class, this.environment, this.resourceLoader, this.registry);configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());} else {// 4. 普通配置类,递归处理this.importStack.registerImport(currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());processConfigurationClass(candidate.asConfigClass(configClass));}}} catch (BeanDefinitionStoreException ex) {throw ex;} finally {this.importStack.pop();}}
}
入口9:DeferredImportSelectorHandler.process()
public void process() {List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;this.deferredImportSelectors = null;try {if (deferredImports != null) {DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();deferredImports.sort(DEFERRED_IMPORT_COMPARATOR);deferredImports.forEach(handler::register);// 处理所有延迟导入选择器(AutoConfigurationImportSelector.selectImports()在这里调用)handler.processGroupImports();}} finally {this.deferredImportSelectors = new ArrayList<>();}
}
入口10:AutoConfigurationImportSelector.selectImports()(最终执行)
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {if (!isEnabled(annotationMetadata)) {return NO_IMPORTS;}// 获取自动配置条目AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);// 返回要导入的配置类名称数组return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
关键时机总结
@EnableAutoConfiguration 的执行时机:
- 时机:在
refresh()
方法的invokeBeanFactoryPostProcessors()
阶段 - 触发器:
ConfigurationClassPostProcessor
(一个BeanDefinitionRegistryPostProcessor
) - 延迟执行:
AutoConfigurationImportSelector
实现了DeferredImportSelector
,会在所有普通配置类解析完成后才执行 - 执行顺序:在所有
@Configuration
类解析完成后,但在Bean实例化之前
为什么要延迟执行?
// 1. DeferredImportSelector 的特殊之处
public interface DeferredImportSelector extends ImportSelector {// 延迟执行,在所有@Configuration解析完成后才执行// 这样可以确保:// - 用户自定义的配置类已经加载// - 可以根据用户配置决定是否启用某些自动配置// - 避免循环依赖问题
}// 2. 执行顺序示例
@SpringBootApplication
public class Application {// 执行顺序:// 1. 先解析 @ComponentScan(扫描用户定义的Bean)// 2. 再解析 @EnableAutoConfiguration(自动配置)// 这样用户的配置可以覆盖自动配置
}
完整调用链路图
用户启动应用↓
SpringApplication.run()↓
【创建ApplicationContext】↓
AbstractApplicationContext.refresh()↓
【第5步】invokeBeanFactoryPostProcessors() ← @EnableAutoConfiguration执行入口↓
PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors()↓
ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry()↓
ConfigurationClassPostProcessor.processConfigBeanDefinitions()↓
ConfigurationClassParser.parse()↓
【解析主配置类】
ConfigurationClassParser.doProcessConfigurationClass()↓
【发现@Import(AutoConfigurationImportSelector.class)】
ConfigurationClassParser.processImports()↓
【识别为DeferredImportSelector,加入延迟队列】
DeferredImportSelectorHandler.handle()↓
【所有配置类解析完成后】
DeferredImportSelectorHandler.process()↓
【调用selectImports方法】
AutoConfigurationImportSelector.selectImports()↓
【加载spring.factories】
SpringFactoriesLoader.loadFactoryNames()↓
【条件过滤】
OnClassCondition.match()↓
【返回有效的自动配置类列表】↓
【注册BeanDefinition】
ConfigurationClassBeanDefinitionReader.loadBeanDefinitions()↓
【第11步】finishBeanFactoryInitialization()↓
【实例化Bean】
DefaultListableBeanFactory.preInstantiateSingletons()
二、生效机制详解
核心问题:@EnableAutoConfiguration 注解通过什么机制让自动配置生效?
生效机制的3个核心要素:
1. @Import 导入机制↓
2. ImportSelector 选择器↓
3. Conditional 条件注解
详细生效流程
第1步:@EnableAutoConfiguration 注解定义
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class) // 关键:通过@Import导入选择器
public @interface EnableAutoConfiguration {String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";// 排除指定的自动配置类Class<?>[] exclude() default {};// 排除指定名称的自动配置类String[] excludeName() default {};
}
生效原理分析:
- @Import 注解:Spring 的核心机制,用于导入配置类
- AutoConfigurationImportSelector:实现了
DeferredImportSelector
接口,负责选择要导入的配置类 - @AutoConfigurationPackage:自动配置包,注册主配置类所在的包
第2步:@Import 机制如何工作
@Import 的三种使用方式:
// 方式1:直接导入配置类
@Import(MyConfiguration.class)
public @interface EnableMyFeature {
}// 方式2:导入 ImportSelector(@EnableAutoConfiguration使用此方式)
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
}// 方式3:导入 ImportBeanDefinitionRegistrar
@Import(MyBeanDefinitionRegistrar.class)
public @interface EnableMyRegistrar {
}
@Import 的处理流程:
// Spring 处理 @Import 注解的核心逻辑
public class ConfigurationClassParser {private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,Collection<SourceClass> importCandidates, boolean checkForCircularImports) {for (SourceClass candidate : importCandidates) {// 判断导入类的类型if (candidate.isAssignable(ImportSelector.class)) {// 类型1:ImportSelectorClass<?> candidateClass = candidate.loadClass();ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);// 调用 Aware 接口invokeAwareMethods(selector);if (selector instanceof DeferredImportSelector) {// AutoConfigurationImportSelector 走这个分支this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);} else {// 普通 ImportSelector 立即执行String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);processImports(configClass, currentSourceClass, importSourceClasses, false);}} else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {// 类型2:ImportBeanDefinitionRegistrarClass<?> candidateClass = candidate.loadClass();ImportBeanDefinitionRegistrar registrar = BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());} else {// 类型3:普通配置类processConfigurationClass(candidate.asConfigClass(configClass));}}}
}
第3步:AutoConfigurationImportSelector 选择逻辑
核心方法:selectImports()
public class AutoConfigurationImportSelector implements DeferredImportSelector,BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {@Overridepublic String[] selectImports(AnnotationMetadata annotationMetadata) {// 1. 检查自动配置功能是否开启if (!isEnabled(annotationMetadata)) {return NO_IMPORTS;}// 2. 获取自动配置的元信息AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);// 3. 返回需要导入的配置类名称数组return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());}protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {if (!isEnabled(annotationMetadata)) {return EMPTY_ENTRY;}// 1. 获取注解属性(exclude、excludeName)AnnotationAttributes attributes = getAttributes(annotationMetadata);// 2. 从 spring.factories 加载候选配置类(核心)List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);// 3. 去重configurations = removeDuplicates(configurations);// 4. 处理排除的配置类Set<String> exclusions = getExclusions(annotationMetadata, attributes);checkExcludedClasses(configurations, exclusions);configurations.removeAll(exclusions);// 5. 过滤不满足条件的配置类(核心)configurations = getConfigurationClassFilter().filter(configurations);// 6. 触发自动配置导入事件fireAutoConfigurationImportEvents(configurations, exclusions);return new AutoConfigurationEntry(configurations, exclusions);}// 加载候选配置类protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {// 从 META-INF/spring.factories 文件加载配置类List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. " +"If you are using a custom packaging, make sure that file is correct.");return configurations;}
}
第4步:SpringFactoriesLoader 加载机制
META-INF/spring.factories 文件格式:
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration
加载源码实现:
public final class SpringFactoriesLoader {public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";// 缓存,避免重复加载private static final Map<ClassLoader, MultiValueMap<String, String>> cache = new ConcurrentReferenceHashMap<>();public static List<String> loadFactoryNames(Class<?> factoryType, ClassLoader classLoader) {String factoryTypeName = factoryType.getName();return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());}private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {// 1. 尝试从缓存获取MultiValueMap<String, String> result = cache.get(classLoader);if (result != null) {return result;}try {// 2. 加载所有 jar 包中的 META-INF/spring.factories 文件Enumeration<URL> urls = (classLoader != null ?classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));result = new LinkedMultiValueMap<>();// 3. 遍历所有 spring.factories 文件while (urls.hasMoreElements()) {URL url = urls.nextElement();UrlResource resource = new UrlResource(url);Properties properties = PropertiesLoaderUtils.loadProperties(resource);// 4. 解析 properties 文件for (Map.Entry<?, ?> entry : properties.entrySet()) {String factoryTypeName = ((String) entry.getKey()).trim();String[] factoryImplementationNames = StringUtils.commaDelimitedListToStringArray((String) entry.getValue());// 5. 添加到结果集for (String factoryImplementationName : factoryImplementationNames) {result.add(factoryTypeName, factoryImplementationName.trim());}}}// 6. 放入缓存cache.put(classLoader, result);return result;} catch (IOException ex) {throw new IllegalArgumentException("Unable to load factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex);}}
}
第5步:条件注解过滤(Conditional)
为什么需要条件注解?
// 问题:如果没有条件注解
@Configuration
public class DataSourceAutoConfiguration {@Beanpublic DataSource dataSource() {// 如果项目中没有引入数据库驱动,这里会报错return new HikariDataSource();}
}// 解决:使用条件注解
@Configuration
@ConditionalOnClass(DataSource.class) // 只有存在 DataSource 类才生效
public class DataSourceAutoConfiguration {@Bean@ConditionalOnMissingBean // 只有容器中不存在 DataSource 才创建public DataSource dataSource() {return new HikariDataSource();}
}
常用条件注解:
// 1. 类条件
@ConditionalOnClass(DataSource.class) // 类路径存在指定类
@ConditionalOnMissingClass("com.example.MyClass") // 类路径不存在指定类// 2. Bean条件
@ConditionalOnBean(DataSource.class) // 容器中存在指定Bean
@ConditionalOnMissingBean(DataSource.class) // 容器中不存在指定Bean// 3. 属性条件
@ConditionalOnProperty(name = "spring.datasource.url") // 存在指定属性
@ConditionalOnProperty(name = "app.feature.enabled", havingValue = "true") // 属性值匹配// 4. 资源条件
@ConditionalOnResource(resources = "classpath:app.properties") // 存在指定资源// 5. Web应用条件
@ConditionalOnWebApplication(type = Type.SERVLET) // 是Servlet Web应用
@ConditionalOnNotWebApplication // 不是Web应用// 6. SpEL条件
@ConditionalOnExpression("${app.enabled:true}") // SpEL表达式为true// 7. 单例条件
@ConditionalOnSingleCandidate(DataSource.class) // 容器中只有一个候选Bean
条件注解的执行原理:
// 条件注解的核心处理类
class OnClassCondition extends FilteringSpringBootCondition {@Overridepublic ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {ClassLoader classLoader = context.getClassLoader();ConditionMessage matchMessage = ConditionMessage.empty();// 1. 获取 @ConditionalOnClass 的值List<String> onClasses = getCandidates(metadata, ConditionalOnClass.class);if (onClasses != null) {// 2. 检查类是否存在List<String> missing = filter(onClasses, ClassNameFilter.MISSING, classLoader);// 3. 如果有类不存在,返回不匹配if (!missing.isEmpty()) {return ConditionOutcome.noMatch(ConditionMessage.forCondition(ConditionalOnClass.class).didNotFind("required class", "required classes").items(Style.QUOTE, missing));}matchMessage = matchMessage.andCondition(ConditionalOnClass.class).found("required class", "required classes").items(Style.QUOTE, filter(onClasses, ClassNameFilter.PRESENT, classLoader));}return ConditionOutcome.match(matchMessage);}// 检查类是否存在protected enum ClassNameFilter {PRESENT {@Overridepublic boolean matches(String className, ClassLoader classLoader) {return isPresent(className, classLoader);}},MISSING {@Overridepublic boolean matches(String className, ClassLoader classLoader) {return !isPresent(className, classLoader);}};abstract boolean matches(String className, ClassLoader classLoader);static boolean isPresent(String className, ClassLoader classLoader) {try {forName(className, classLoader);return true;} catch (Throwable ex) {return false;}}}
}
第6步:完整的自动配置示例
以 DataSource 自动配置为例:
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })
@ConditionalOnMissingBean(type = "io.r2dbc.spi.ConnectionFactory")
@EnableConfigurationProperties(DataSourceProperties.class)
@Import({ DataSourcePoolMetadataProvidersConfiguration.class,DataSourceInitializationConfiguration.class })
public class DataSourceAutoConfiguration {// 1. 内部配置类:HikariDataSource 配置@Configuration(proxyBeanMethods = false)@ConditionalOnClass(HikariDataSource.class)@ConditionalOnMissingBean(DataSource.class)@ConditionalOnProperty(name = "spring.datasource.type", havingValue = "com.zaxxer.hikari.HikariDataSource", matchIfMissing = true)static class Hikari {@Bean@ConfigurationProperties(prefix = "spring.datasource.hikari")HikariDataSource dataSource(DataSourceProperties properties) {HikariDataSource dataSource = createDataSource(properties, HikariDataSource.class);if (StringUtils.hasText(properties.getName())) {dataSource.setPoolName(properties.getName());}return dataSource;}}// 2. 内部配置类:Tomcat DataSource 配置@Configuration(proxyBeanMethods = false)@ConditionalOnClass(org.apache.tomcat.jdbc.pool.DataSource.class)@ConditionalOnMissingBean(DataSource.class)@ConditionalOnProperty(name = "spring.datasource.type", havingValue = "org.apache.tomcat.jdbc.pool.DataSource")static class Tomcat {@Bean@ConfigurationProperties(prefix = "spring.datasource.tomcat")org.apache.tomcat.jdbc.pool.DataSource dataSource(DataSourceProperties properties) {org.apache.tomcat.jdbc.pool.DataSource dataSource = createDataSource(properties, org.apache.tomcat.jdbc.pool.DataSource.class);return dataSource;}}// 3. 通用的 DataSource 配置@Configuration(proxyBeanMethods = false)@ConditionalOnMissingBean(DataSource.class)@ConditionalOnProperty(name = "spring.datasource.type")static class Generic {@BeanDataSource dataSource(DataSourceProperties properties) {// 根据配置文件中的 spring.datasource.type 创建 DataSourcereturn properties.initializeDataSourceBuilder().build();}}
}
配置属性类:
@ConfigurationProperties(prefix = "spring.datasource")
public class DataSourceProperties implements BeanClassLoaderAware, InitializingBean {private ClassLoader classLoader;/*** 数据源类型,如果不指定,会根据类路径自动检测*/private Class<? extends DataSource> type;/*** JDBC 驱动类名*/private String driverClassName;/*** JDBC URL*/private String url;/*** 用户名*/private String username;/*** 密码*/private String password;// getters and setters...
}
生效机制总结图
@EnableAutoConfiguration↓
【通过 @Import 导入】↓
AutoConfigurationImportSelector↓
【实现 DeferredImportSelector 接口】↓
selectImports() 方法被调用↓
【从 META-INF/spring.factories 加载】↓
SpringFactoriesLoader.loadFactoryNames()↓
【返回所有自动配置类列表】↓
【条件注解过滤】
@ConditionalOnClass
@ConditionalOnMissingBean
@ConditionalOnProperty↓
【返回满足条件的配置类】↓
【注册 BeanDefinition】
ConfigurationClassBeanDefinitionReader↓
【创建 Bean 实例】
AbstractBeanFactory.getBean()↓
【自动配置生效】
关键机制总结
@EnableAutoConfiguration 生效的关键机制:
-
@Import 机制
- 通过
@Import(AutoConfigurationImportSelector.class)
导入选择器 - Spring 在处理配置类时会识别
@Import
注解
- 通过
-
ImportSelector 接口
AutoConfigurationImportSelector
实现了DeferredImportSelector
selectImports()
方法返回要导入的配置类名称数组
-
SPI 机制
- 通过
META-INF/spring.factories
文件定义自动配置类 SpringFactoriesLoader
加载所有 jar 包中的配置
- 通过
-
条件注解
@ConditionalOnXxx
注解决定配置是否生效- 在配置类注册前进行条件判断
-
配置属性绑定
@ConfigurationProperties
绑定配置文件属性@EnableConfigurationProperties
启用属性配置
生效流程简述:
- 解析
@EnableAutoConfiguration
注解 - 识别
@Import(AutoConfigurationImportSelector.class)
- 调用
AutoConfigurationImportSelector.selectImports()
- 从
spring.factories
加载所有自动配置类 - 通过条件注解过滤不满足条件的配置类
- 注册满足条件的配置类到容器
- 创建配置类中定义的 Bean
19. Spring Boot 的配置文件优先级?
答案:
配置文件加载优先级(从高到低):
- 命令行参数
- JNDI 属性
- 系统属性
- 环境变量
application-{profile}.properties/yml
application.properties/yml
@PropertySource
注解- 默认属性
示例:
# application.yml
server:port: 8080# application-dev.yml
server:port: 8081# application-prod.yml
server:port: 8082
20. Spring 如何解析配置文件?源码流程详解
答案: Spring 通过多种方式解析配置文件,支持 XML、Properties、YAML 等格式。
2分钟面试表达:
Spring配置文件解析分为三个核心阶段:加载、解析、注入。首先,Spring通过PropertySource体系加载配置文件,支持XML、Properties、YAML等多种格式。然后通过PropertyResolver接口解析配置值,支持占位符、默认值、SpEL表达式等高级特性。最后通过@Value注解或@ConfigurationProperties将配置注入到Bean中。整个流程采用责任链模式,支持配置优先级和条件化加载。
源码解析流程:
1. XML配置文件解析源码流程
核心类:ClassPathXmlApplicationContext
// 1. 入口:ClassPathXmlApplicationContext构造器
public ClassPathXmlApplicationContext(String configLocation) throws BeansException {this(new String[] {configLocation}, true, null);
}// 2. 调用父类AbstractXmlApplicationContext的构造器
public AbstractXmlApplicationContext(ApplicationContext parent) {super(parent);
}// 3. 核心方法:loadBeanDefinitions
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {// 创建XML Bean定义读取器XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);// 配置读取器beanDefinitionReader.setEnvironment(this.getEnvironment());beanDefinitionReader.setResourceLoader(this);beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));// 初始化读取器initBeanDefinitionReader(beanDefinitionReader);// 加载Bean定义loadBeanDefinitions(beanDefinitionReader);
}// 4. 加载Bean定义的具体实现
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {Resource[] configResources = getConfigResources();if (configResources != null) {reader.loadBeanDefinitions(configResources);}String[] configLocations = getConfigLocations();if (configLocations != null) {reader.loadBeanDefinitions(configLocations);}
}
XML解析核心:XmlBeanDefinitionReader
// 5. XmlBeanDefinitionReader.loadBeanDefinitions
@Override
public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {return loadBeanDefinitions(location, null);
}// 6. 解析XML文档
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)throws BeanDefinitionStoreException {try {// 解析XML文档Document doc = doLoadDocument(inputSource, resource);// 注册Bean定义return registerBeanDefinitions(doc, resource);} catch (BeanDefinitionStoreException ex) {throw ex;}
}// 7. 注册Bean定义
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();int countBefore = getRegistry().getBeanDefinitionCount();documentReader.registerBeanDefinitions(doc, createReaderContext(resource));return getRegistry().getBeanDefinitionCount() - countBefore;
}
2. Properties配置文件解析源码流程
核心类:PropertySourcesPlaceholderConfigurer
// 1. 属性占位符配置器
public class PropertySourcesPlaceholderConfigurer extends PlaceholderConfigurerSupportimplements EnvironmentAware {private PropertySources propertySources;private Environment environment;// 2. 处理属性占位符@Overrideprotected void processProperties(ConfigurableListableBeanFactory beanFactory, Properties props) throws BeansException {// 创建属性源MutablePropertySources propertySources = new MutablePropertySources();if (this.propertySources != null) {propertySources.addLast(this.propertySources);}// 添加环境属性源if (this.environment != null) {propertySources.addLast(new PropertiesPropertySource("environment", this.environment.getSystemProperties()));}// 处理占位符processProperties(beanFactory, new PropertySourcesPropertyResolver(propertySources));}
}
属性解析核心:PropertySourcesPropertyResolver
// 3. 属性源属性解析器
public class PropertySourcesPropertyResolver extends AbstractPropertyResolver {private final PropertySources propertySources;public PropertySourcesPropertyResolver(PropertySources propertySources) {this.propertySources = propertySources;}// 4. 解析属性值@Overrideprotected String getPropertyAsRawString(String key) {return getProperty(key, String.class, false);}// 5. 获取属性值protected <T> T getProperty(String key, Class<T> targetValueType, boolean resolveNestedPlaceholders) {if (this.propertySources != null) {for (PropertySource<?> propertySource : this.propertySources) {Object value = propertySource.getProperty(key);if (value != null) {if (resolveNestedPlaceholders && value instanceof String) {value = resolveNestedPlaceholders((String) value);}return convertValueIfNecessary(value, targetValueType);}}}return null;}
}
3. YAML配置文件解析源码流程
核心类:YamlPropertiesFactoryBean
// 1. YAML属性工厂Bean
public class YamlPropertiesFactoryBean implements FactoryBean<Properties>, InitializingBean {private Resource[] resources = new Resource[0];private Yaml yaml;// 2. 创建YAML解析器@Overridepublic void afterPropertiesSet() throws Exception {if (this.yaml == null) {this.yaml = createYaml();}}// 3. 解析YAML文件@Overridepublic Properties getObject() throws Exception {Properties result = new Properties();if (!ObjectUtils.isEmpty(this.resources)) {for (Resource resource : this.resources) {if (resource.exists()) {// 解析YAML内容Map<String, Object> map = this.yaml.loadAs(new InputStreamReader(resource.getInputStream(), "UTF-8"), Map.class);// 转换为PropertiesPropertiesLoaderUtils.fillProperties(result, map);}}}return result;}
}
4. 配置注入源码流程
@Value注解处理:AutowiredAnnotationBeanPostProcessor
// 1. 自动装配注解Bean后处理器
public class AutowiredAnnotationBeanPostProcessor implements BeanPostProcessor, MergedBeanDefinitionPostProcessor, PriorityOrdered, BeanFactoryAware {// 2. 处理属性注入@Overridepublic PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);try {metadata.inject(bean, beanName, pvs);} catch (BeanCreationException ex) {throw ex;}return pvs;}// 3. 注入属性值@Overrideprotected void inject(Object bean, String beanName, PropertyValues pvs) throws Throwable {Field field = (Field) this.member;Object value;if (this.cached) {value = resolvedCachedArgument(beanName, this.cachedFieldValue);} else {// 解析属性值value = resolveFieldValue(field, bean, beanName);}if (value != null) {ReflectionUtils.makeAccessible(field);field.set(bean, value);}}
}
@ConfigurationProperties处理:ConfigurationPropertiesBindingPostProcessor
// 4. 配置属性绑定后处理器
public class ConfigurationPropertiesBindingPostProcessor implements BeanPostProcessor, PriorityOrdered, ApplicationContextAware, InitializingBean {// 5. 绑定配置属性@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {ConfigurationProperties annotation = AnnotationUtils.findAnnotation(bean.getClass(), ConfigurationProperties.class);if (annotation != null) {// 绑定属性bind(bean, annotation);}return bean;}// 6. 执行属性绑定private void bind(Object bean, ConfigurationProperties annotation) {// 创建绑定器Binder binder = new Binder(ConfigurationPropertySources.from(environment));// 绑定属性BindResult<?> result = binder.bind(annotation.prefix(), bean.getClass());if (result.isBound()) {// 将绑定结果设置到Bean中BeanUtils.copyProperties(result.get(), bean);}}
}
5. 配置优先级源码实现
PropertySource体系
// 1. 属性源抽象类
public abstract class PropertySource<T> {protected final String name;protected final T source;public PropertySource(String name, T source) {this.name = name;this.source = source;}public abstract Object getProperty(String name);// 2. 属性源比较(用于优先级排序)@Overridepublic boolean equals(Object obj) {return (this == obj || (obj instanceof PropertySource &&ObjectUtils.nullSafeEquals(this.name, ((PropertySource<?>) obj).name)));}
}// 3. 可变的属性源集合
public class MutablePropertySources implements PropertySources {private final List<PropertySource<?>> propertySourceList = new CopyOnWriteArrayList<>();// 4. 添加属性源(按优先级)public void addFirst(PropertySource<?> propertySource) {removeIfPresent(propertySource);this.propertySourceList.add(0, propertySource);}public void addLast(PropertySource<?> propertySource) {removeIfPresent(propertySource);this.propertySourceList.add(propertySource);}
}
配置优先级实现
// 5. 标准环境实现
public class StandardEnvironment extends AbstractEnvironment {public static final String SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME = "systemEnvironment";public static final String SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME = "systemProperties";// 6. 自定义属性源@Overrideprotected void customizePropertySources(MutablePropertySources propertySources) {// 系统属性(高优先级)propertySources.addLast(new PropertiesPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties()));// 系统环境变量(低优先级)propertySources.addLast(new SystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment()));}
}
6. 占位符解析源码
占位符解析器
// 1. 属性占位符解析器
public class PropertyPlaceholderHelper {private static final String PLACEHOLDER_PREFIX = "${";private static final String PLACEHOLDER_SUFFIX = "}";private static final String VALUE_SEPARATOR = ":";// 2. 解析占位符public String replacePlaceholders(String value, PlaceholderResolver placeholderResolver) {return parseStringValue(value, placeholderResolver, new HashSet<>());}// 3. 解析字符串值protected String parseStringValue(String strVal, PlaceholderResolver placeholderResolver, Set<String> visitedPlaceholders) {StringBuilder result = new StringBuilder(strVal);int startIndex = strVal.indexOf(PLACEHOLDER_PREFIX);while (startIndex != -1) {int endIndex = findPlaceholderEndIndex(result, startIndex);if (endIndex != -1) {// 提取占位符String placeholder = result.substring(startIndex + PLACEHOLDER_PREFIX.length(), endIndex);String originalPlaceholder = placeholder;if (!visitedPlaceholders.add(originalPlaceholder)) {throw new IllegalArgumentException("Circular placeholder reference '" + originalPlaceholder + "' in property definitions");}// 解析占位符值String propVal = placeholderResolver.resolvePlaceholder(placeholder);if (propVal == null && VALUE_SEPARATOR != null) {int separatorIndex = placeholder.indexOf(VALUE_SEPARATOR);if (separatorIndex != -1) {String actualPlaceholder = placeholder.substring(0, separatorIndex);String defaultValue = placeholder.substring(separatorIndex + VALUE_SEPARATOR.length());propVal = placeholderResolver.resolvePlaceholder(actualPlaceholder);if (propVal == null) {propVal = defaultValue;}}}if (propVal != null) {// 递归解析嵌套占位符propVal = parseStringValue(propVal, placeholderResolver, visitedPlaceholders);result.replace(startIndex, endIndex + PLACEHOLDER_SUFFIX.length(), propVal);startIndex = result.indexOf(PLACEHOLDER_PREFIX, startIndex + propVal.length());} else {startIndex = result.indexOf(PLACEHOLDER_PREFIX, endIndex + PLACEHOLDER_SUFFIX.length());}visitedPlaceholders.remove(originalPlaceholder);} else {startIndex = -1;}}return result.toString();}
}
源码流程总结:
- 加载阶段:通过ResourceLoader加载配置文件,创建对应的PropertySource
- 解析阶段:使用PropertyResolver解析配置值,支持占位符、默认值、SpEL表达式
- 注入阶段:通过BeanPostProcessor将配置注入到Bean中
- 优先级:通过PropertySources的List顺序实现配置优先级
- 缓存:解析结果会被缓存,提高性能
Spring 设计模式
21. Spring 中使用了哪些设计模式?
答案:
Spring 框架大量使用了设计模式,体现了优秀框架的设计思想:
1. 单例模式 (Singleton Pattern)
// Spring 默认的 Bean 作用域就是单例
@Service
public class UserService {// Spring 容器中只有一个 UserService 实例
}// 配置单例模式
@Bean
@Scope("singleton") // 默认就是单例
public UserService userService() {return new UserService();
}
2. 工厂模式 (Factory Pattern)
// BeanFactory 和 ApplicationContext 都是工厂
public interface BeanFactory {Object getBean(String name) throws BeansException;<T> T getBean(String name, Class<T> requiredType) throws BeansException;
}// 使用工厂获取 Bean
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
UserService userService = context.getBean("userService", UserService.class);
3. 代理模式 (Proxy Pattern)
// AOP 使用代理模式
@Aspect
@Component
public class LoggingAspect {@Before("execution(* com.example.service.*.*(..))")public void beforeMethod(JoinPoint joinPoint) {System.out.println("方法执行前");}
}// Spring 为 UserService 创建代理对象
@Service
public class UserService {public void saveUser(User user) {// 实际调用的是代理对象的方法}
}
4. 模板方法模式 (Template Method Pattern)
// JdbcTemplate 使用模板方法模式
@Repository
public class UserDao {@Autowiredprivate JdbcTemplate jdbcTemplate;public User findById(Long id) {return jdbcTemplate.queryForObject("SELECT * FROM users WHERE id = ?",new Object[]{id},(rs, rowNum) -> new User(rs.getLong("id"), rs.getString("name")));}
}// 模板方法定义算法骨架
public abstract class JdbcTemplate {public final <T> T query(String sql, RowMapper<T> rowMapper) {// 1. 获取连接Connection conn = getConnection();try {// 2. 创建语句PreparedStatement stmt = conn.prepareStatement(sql);// 3. 执行查询ResultSet rs = stmt.executeQuery();// 4. 处理结果return rowMapper.mapRow(rs);} finally {// 5. 关闭资源closeConnection(conn);}}
}
5. 观察者模式 (Observer Pattern)
// Spring 事件机制使用观察者模式
// 发布事件
@Component
public class UserService {@Autowiredprivate ApplicationEventPublisher eventPublisher;public void createUser(User user) {userRepository.save(user);// 发布用户创建事件eventPublisher.publishEvent(new UserCreatedEvent(user));}
}// 监听事件
@Component
public class UserEventListener {@EventListenerpublic void handleUserCreated(UserCreatedEvent event) {System.out.println("用户创建事件: " + event.getUser());}
}
6. 策略模式 (Strategy Pattern)
// 不同的数据源策略
public interface DataSourceStrategy {Connection getConnection();
}@Component
public class MySQLDataSourceStrategy implements DataSourceStrategy {@Overridepublic Connection getConnection() {return DriverManager.getConnection("jdbc:mysql://localhost:3306/db");}
}@Component
public class PostgreSQLDataSourceStrategy implements DataSourceStrategy {@Overridepublic Connection getConnection() {return DriverManager.getConnection("jdbc:postgresql://localhost:5432/db");}
}@Service
public class DataService {@Autowiredprivate List<DataSourceStrategy> strategies;public void processData(String dbType) {DataSourceStrategy strategy = strategies.stream().filter(s -> s.getClass().getSimpleName().contains(dbType)).findFirst().orElseThrow(() -> new IllegalArgumentException("不支持的数据源"));Connection conn = strategy.getConnection();// 处理数据}
}
7. 适配器模式 (Adapter Pattern)
// HandlerAdapter 使用适配器模式
public interface HandlerAdapter {boolean supports(Object handler);ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
}@Component
public class RequestMappingHandlerAdapter implements HandlerAdapter {@Overridepublic boolean supports(Object handler) {return handler instanceof HandlerMethod;}@Overridepublic ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {HandlerMethod handlerMethod = (HandlerMethod) handler;// 调用实际的 Controller 方法return invokeHandlerMethod(request, response, handlerMethod);}
}
8. 装饰器模式 (Decorator Pattern)
// Bean 的装饰器模式
public class BeanWrapperImpl implements BeanWrapper {private Object wrappedObject;public BeanWrapperImpl(Object object) {this.wrappedObject = object;}@Overridepublic void setPropertyValue(String propertyName, Object value) {// 装饰原有的对象,添加属性设置功能PropertyDescriptor pd = getPropertyDescriptor(propertyName);Method writeMethod = pd.getWriteMethod();try {writeMethod.invoke(wrappedObject, value);} catch (Exception e) {throw new BeanException("设置属性失败", e);}}
}
9. 建造者模式 (Builder Pattern)
// Spring Boot 的配置构建
@ConfigurationProperties(prefix = "spring.datasource")
public class DataSourceProperties {private String url;private String username;private String password;// Builder 模式public static class Builder {private DataSourceProperties properties = new DataSourceProperties();public Builder url(String url) {properties.url = url;return this;}public Builder username(String username) {properties.username = username;return this;}public Builder password(String password) {properties.password = password;return this;}public DataSourceProperties build() {return properties;}}
}
10. 责任链模式 (Chain of Responsibility Pattern)
// Spring Security 的过滤器链
public class FilterChainProxy implements Filter {private List<SecurityFilterChain> filterChains;@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {HttpServletRequest httpRequest = (HttpServletRequest) request;// 找到匹配的过滤器链for (SecurityFilterChain filterChain : filterChains) {if (filterChain.matches(httpRequest)) {// 执行过滤器链filterChain.doFilter(request, response, chain);return;}}chain.doFilter(request, response);}
}
设计模式总结:
- 单例模式:Bean 的默认作用域
- 工厂模式:BeanFactory 和 ApplicationContext
- 代理模式:AOP 实现
- 模板方法模式:JdbcTemplate、RestTemplate
- 观察者模式:事件发布订阅机制
- 策略模式:多种数据源、缓存策略
- 适配器模式:HandlerAdapter
- 装饰器模式:BeanWrapper
- 建造者模式:配置构建
- 责任链模式:过滤器链
Spring Security
22. Spring Security 的核心概念?
答案:
核心组件:
- Authentication: 认证(你是谁)
- Authorization: 授权(你能做什么)
- Principal: 主体(用户)
- GrantedAuthority: 权限
- SecurityContext: 安全上下文
认证流程:
- 用户提交凭证
- AuthenticationManager 验证
- AuthenticationProvider 处理认证
- 认证成功创建 Authentication 对象
- 存储到 SecurityContext
23. Spring Security 的配置方式?
答案:
基于注解:
@EnableWebSecurity
@Configuration
public class SecurityConfig {@Beanpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {http.authorizeHttpRequests(authz -> authz.requestMatchers("/public/**").permitAll().requestMatchers("/admin/**").hasRole("ADMIN").anyRequest().authenticated()).formLogin(form -> form.loginPage("/login").defaultSuccessUrl("/dashboard")).logout(logout -> logout.logoutUrl("/logout").logoutSuccessUrl("/login"));return http.build();}
}
方法级安全:
@Service
public class UserService {@PreAuthorize("hasRole('ADMIN')")public void deleteUser(Long id) {// 只有 ADMIN 角色可以执行}@PostAuthorize("returnObject.owner == authentication.name")public User getUser(Long id) {// 只能访问自己的用户信息return userRepository.findById(id);}
}
Spring Data
24. Spring Data JPA 的优势?
答案:
核心优势:
- Repository 抽象: 简化数据访问层
- 方法命名查询: 根据方法名自动生成查询
- @Query 注解: 自定义 JPQL 或原生 SQL
- 分页和排序: 内置分页支持
- 审计功能: 自动记录创建/修改时间
示例:
@Repository
public interface UserRepository extends JpaRepository<User, Long> {// 方法命名查询List<User> findByName(String name);List<User> findByNameAndAge(String name, Integer age);// 自定义查询@Query("SELECT u FROM User u WHERE u.email = ?1")User findByEmail(String email);// 原生 SQL@Query(value = "SELECT * FROM users WHERE age > ?1", nativeQuery = true)List<User> findByAgeGreaterThan(Integer age);// 分页查询Page<User> findByNameContaining(String name, Pageable pageable);
}
25. Spring Data 的事务管理?
答案:
声明式事务:
@Service
@Transactional
public class UserService {@Autowiredprivate UserRepository userRepository;@Transactional(readOnly = true)public User findById(Long id) {return userRepository.findById(id).orElse(null);}@Transactional(rollbackFor = Exception.class)public User save(User user) {return userRepository.save(user);}@Transactional(propagation = Propagation.REQUIRES_NEW)public void logUserAction(String action) {// 新事务中记录日志}
}
事务传播行为:
REQUIRED
: 默认,加入现有事务或创建新事务REQUIRES_NEW
: 总是创建新事务SUPPORTS
: 支持事务,没有事务也能执行NOT_SUPPORTED
: 不支持事务MANDATORY
: 必须在事务中执行NEVER
: 不能在事务中执行NESTED
: 嵌套事务
Spring Cloud
26. Spring Cloud 的核心组件?
答案:
服务发现:
- Eureka: 服务注册与发现
- Consul: 多数据中心服务发现
- Zookeeper: 分布式协调服务
负载均衡:
- Ribbon: 客户端负载均衡
- LoadBalancer: Spring Cloud LoadBalancer
服务调用:
- Feign: 声明式 HTTP 客户端
- OpenFeign: Feign 的增强版
服务网关:
- Gateway: 响应式网关
- Zuul: 传统网关
配置管理:
- Config: 分布式配置中心
熔断器:
- Hystrix: 熔断器模式实现
- Resilience4j: 轻量级熔断器
27. Spring Cloud Gateway 的工作原理?
答案:
核心概念:
- Route: 路由,定义请求如何转发
- Predicate: 断言,匹配请求条件
- Filter: 过滤器,处理请求和响应
工作流程:
- 客户端发送请求到 Gateway
- Gateway Handler Mapping 查找匹配的路由
- Gateway Web Handler 处理请求
- 执行过滤器链(Pre Filter)
- 代理到目标服务
- 执行过滤器链(Post Filter)
- 返回响应给客户端
配置示例:
spring:cloud:gateway:routes:- id: user-serviceuri: lb://user-servicepredicates:- Path=/api/users/**filters:- StripPrefix=2- AddRequestHeader=X-Gateway, Spring-Cloud
事务管理
28. Spring 事务管理的两种方式?
答案:
编程式事务:
@Service
public class UserService {@Autowiredprivate TransactionTemplate transactionTemplate;public void transferMoney(Long fromId, Long toId, BigDecimal amount) {transactionTemplate.execute(status -> {try {// 业务逻辑accountService.debit(fromId, amount);accountService.credit(toId, amount);return null;} catch (Exception e) {status.setRollbackOnly();throw e;}});}
}
声明式事务:
@Service
@Transactional
public class UserService {@Transactional(rollbackFor = Exception.class)public void transferMoney(Long fromId, Long toId, BigDecimal amount) {accountService.debit(fromId, amount);accountService.credit(toId, amount);}
}
29. Spring 事务的传播行为和隔离级别?
答案:
事务传播行为(Propagation):
- REQUIRED(默认):如果存在事务则加入,否则创建新事务
- REQUIRES_NEW:总是创建新事务,挂起当前事务
- SUPPORTS:如果存在事务则加入,否则以非事务方式执行
- NOT_SUPPORTED:以非事务方式执行,挂起当前事务
- MANDATORY:必须在事务中执行,否则抛出异常
- NEVER:必须在非事务中执行,否则抛出异常
- NESTED:嵌套事务,如果存在事务则创建嵌套事务
传播行为示例:
@Service
public class UserService {@Transactional(propagation = Propagation.REQUIRED)public void methodA() {// 如果调用方有事务则加入,否则创建新事务methodB();}@Transactional(propagation = Propagation.REQUIRES_NEW)public void methodB() {// 总是创建新事务,挂起methodA的事务// 即使methodA回滚,methodB也会提交}@Transactional(propagation = Propagation.NESTED)public void methodC() {// 嵌套事务,如果methodA回滚,methodC也会回滚// 但methodC回滚不会影响methodA}
}
事务隔离级别(Isolation):
- READ_UNCOMMITTED(读未提交):最低级别,可能读到脏数据
- READ_COMMITTED(读已提交):避免脏读,可能出现不可重复读
- REPEATABLE_READ(可重复读):避免脏读和不可重复读,可能出现幻读
- SERIALIZABLE(串行化):最高级别,完全避免并发问题
隔离级别示例:
@Service
public class UserService {@Transactional(isolation = Isolation.READ_COMMITTED)public void readUser() {// 读已提交,避免脏读User user = userRepository.findById(1L);}@Transactional(isolation = Isolation.REPEATABLE_READ)public void updateUser() {// 可重复读,避免不可重复读User user1 = userRepository.findById(1L);// 其他事务修改了数据User user2 = userRepository.findById(1L); // user1 == user2}@Transactional(isolation = Isolation.SERIALIZABLE)public void criticalOperation() {// 串行化,完全避免并发问题,但性能最差}
}
并发问题详解:
脏读(Dirty Read):
// 事务A修改数据但未提交
UPDATE user SET name = 'new_name' WHERE id = 1;// 事务B读取到未提交的数据(脏数据)
SELECT name FROM user WHERE id = 1; // 返回 'new_name'// 事务A回滚,但事务B已经读到了脏数据
不可重复读(Non-Repeatable Read):
// 事务A第一次读取
SELECT name FROM user WHERE id = 1; // 返回 'old_name'// 事务B修改并提交
UPDATE user SET name = 'new_name' WHERE id = 1;
COMMIT;// 事务A第二次读取
SELECT name FROM user WHERE id = 1; // 返回 'new_name'(不一致)
幻读(Phantom Read):
// 事务A第一次查询
SELECT COUNT(*) FROM user WHERE age > 18; // 返回 100// 事务B插入新数据并提交
INSERT INTO user (name, age) VALUES ('new_user', 20);
COMMIT;// 事务A第二次查询
SELECT COUNT(*) FROM user WHERE age > 18; // 返回 101(幻读)
实际应用场景:
@Service
public class OrderService {// 订单创建:需要新事务,避免影响其他操作@Transactional(propagation = Propagation.REQUIRES_NEW)public void createOrder(Order order) {orderRepository.save(order);}// 用户查询:读已提交,避免脏读@Transactional(isolation = Isolation.READ_COMMITTED, readOnly = true)public User getUser(Long id) {return userRepository.findById(id);}// 财务操作:串行化,确保数据一致性@Transactional(isolation = Isolation.SERIALIZABLE)public void transferMoney(Long fromId, Long toId, BigDecimal amount) {// 转账操作}
}
30. @Transactional 注解的失效场景?
答案:
常见失效场景:
- 方法不是 public - 事务代理只能拦截public方法
- 同类内部调用 - 绕过代理,直接调用方法
- 异常被捕获 - 异常被try-catch捕获,事务无法回滚
- Bean 没有被 Spring 管理 - 没有@Component等注解
- 事务传播行为设置错误 - 如设置为NOT_SUPPORTED
示例:
@Service
public class UserService {// 失效1:同类内部调用public void methodA() {this.methodB(); // 不会开启事务,绕过代理}@Transactionalpublic void methodB() {// 事务不会生效}// 失效2:异常被捕获@Transactionalpublic void methodC() {try {// 业务逻辑throw new RuntimeException();} catch (Exception e) {// 异常被捕获,事务不会回滚}}// 失效3:方法不是public@Transactionalprivate void methodD() {// 事务不会生效,代理无法拦截private方法}// 失效4:事务传播行为设置错误@Transactional(propagation = Propagation.NOT_SUPPORTED)public void methodE() {// 不支持事务,即使有@Transactional也不会生效}
}// 失效5:Bean没有被Spring管理
public class UserService { // 没有@Service注解@Transactionalpublic void methodF() {// 事务不会生效,Spring无法管理这个Bean}
}
Bean 生命周期
31. Spring Bean 的生命周期?
答案:
完整生命周期:
- 实例化: 创建 Bean 实例
- 属性填充: 注入依赖
- BeanNameAware.setBeanName()
- BeanFactoryAware.setBeanFactory()
- ApplicationContextAware.setApplicationContext()
- BeanPostProcessor.postProcessBeforeInitialization()
- @PostConstruct 或 InitializingBean.afterPropertiesSet()
- 自定义 init-method
- BeanPostProcessor.postProcessAfterInitialization()
- Bean 就绪,可以使用
- @PreDestroy 或 DisposableBean.destroy()
- 自定义 destroy-method
示例:
@Component
public class MyBean implements BeanNameAware, InitializingBean, DisposableBean {@PostConstructpublic void init() {System.out.println("PostConstruct");}@PreDestroypublic void cleanup() {System.out.println("PreDestroy");}@Overridepublic void setBeanName(String name) {System.out.println("BeanName: " + name);}@Overridepublic void afterPropertiesSet() throws Exception {System.out.println("afterPropertiesSet");}@Overridepublic void destroy() throws Exception {System.out.println("destroy");}
}
32. Bean 的作用域有哪些?
答案:
Spring Bean 作用域:
- singleton: 单例(默认)
- prototype: 原型,每次获取都创建新实例
- request: Web 环境,每个 HTTP 请求一个实例
- session: Web 环境,每个 HTTP 会话一个实例
- application: Web 环境,每个 ServletContext 一个实例
- websocket: WebSocket 环境
配置方式:
@Component
@Scope("prototype")
public class PrototypeBean {// 每次获取都是新实例
}@Component
@Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class RequestScopedBean {// 每个请求一个实例
}
高级特性
33. Spring 的事件机制?
答案:
事件发布:
@Service
public class UserService {@Autowiredprivate ApplicationEventPublisher eventPublisher;public void createUser(User user) {// 创建用户userRepository.save(user);// 发布事件eventPublisher.publishEvent(new UserCreatedEvent(user));}
}
事件监听:
@Component
public class UserEventListener {@EventListenerpublic void handleUserCreated(UserCreatedEvent event) {System.out.println("用户创建事件: " + event.getUser());}@Async@EventListenerpublic void handleUserCreatedAsync(UserCreatedEvent event) {// 异步处理事件}
}
自定义事件:
public class UserCreatedEvent extends ApplicationEvent {private final User user;public UserCreatedEvent(Object source, User user) {super(source);this.user = user;}public User getUser() {return user;}
}
34. Spring 的循环依赖问题?
答案:
Spring的循环依赖是指两个或多个Bean相互依赖,形成循环引用。Spring通过三级缓存机制解决单例Bean的Setter注入循环依赖问题。
核心原理:当创建Bean A时,发现它依赖Bean B,此时A还未完全初始化,但Spring会将A的早期引用(半成品对象)放入三级缓存。然后去创建Bean B,B需要注入A时,从三级缓存中获取A的早期引用,完成B的创建。最后再完成A的初始化。这样就打破了循环。
三级缓存:一级缓存存储完整Bean,二级缓存存储早期Bean引用,三级缓存存储Bean工厂。
重要限制:只能解决单例Bean的Setter注入,构造器注入和原型Bean的循环依赖无法解决,因为构造器注入时对象还未创建,无法提前暴露引用。
面试加分项:可以提及为什么需要三级缓存而不是二级,原因是为了处理AOP代理,确保注入的是代理对象而不是原始对象。
详细技术说明:
什么是循环依赖?
循环依赖是指两个或多个Bean相互依赖,形成循环引用。比如A依赖B,B又依赖A。
循环依赖类型:
- 构造器循环依赖: 无法解决,会抛出异常
- Setter循环依赖: 可以解决(通过三级缓存)
- 原型Bean循环依赖: 无法解决
问题示例:
@Service
public class UserService {@Autowiredprivate OrderService orderService; // UserService依赖OrderServicepublic void createUser() {orderService.createOrder();}
}@Service
public class OrderService {@Autowiredprivate UserService userService; // OrderService依赖UserServicepublic void createOrder() {userService.createUser();}
}
Spring解决机制(三级缓存):
第1步:创建UserService实例
// Spring容器开始创建UserService
UserService userService = new UserService(); // 创建实例,但未完成初始化
第2步:发现循环依赖
// 尝试注入OrderService时发现循环依赖
// 将UserService放入三级缓存(早期暴露)
earlySingletonObjects.put("userService", userService);
第3步:创建OrderService实例
// 创建OrderService实例
OrderService orderService = new OrderService();
第4步:注入UserService
// 从三级缓存获取UserService(早期暴露的版本)
orderService.setUserService(earlySingletonObjects.get("userService"));
第5步:完成初始化
// 完成OrderService初始化
// 完成UserService初始化
三级缓存详解:
- 一级缓存(singletonObjects): 存储完整的单例Bean
- 二级缓存(earlySingletonObjects): 存储早期暴露的单例Bean
- 三级缓存(singletonFactories): 存储单例Bean工厂
为什么构造器循环依赖无法解决?
@Service
public class ServiceA {private ServiceB serviceB;public ServiceA(ServiceB serviceB) { // 构造器注入this.serviceB = serviceB;}
}@Service
public class ServiceB {private ServiceA serviceA;public ServiceB(ServiceA serviceA) { // 构造器注入this.serviceA = serviceA;}
}
构造器注入时,Bean还没有创建完成,无法放入缓存,所以无法解决循环依赖。
面试要点:
- 只有单例Bean的Setter注入可以解决循环依赖
- 三级缓存机制是Spring解决循环依赖的核心
- 构造器注入和原型Bean的循环依赖无法解决
三级缓存源码实现(DefaultSingletonBeanRegistry):
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {// 一级缓存:存储完整的单例Bean(成品对象)private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);// 二级缓存:存储早期的单例Bean引用(半成品对象,已实例化但未初始化)private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);// 三级缓存:存储单例Bean工厂(用于创建早期引用)private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);// 获取单例Bean(核心方法)protected Object getSingleton(String beanName, boolean allowEarlyReference) {// 1. 从一级缓存获取完整BeanObject singletonObject = this.singletonObjects.get(beanName);// 2. 如果一级缓存没有,且Bean正在创建中if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {// 3. 从二级缓存获取早期Bean引用singletonObject = this.earlySingletonObjects.get(beanName);// 4. 如果二级缓存也没有,且允许早期引用if (singletonObject == null && allowEarlyReference) {synchronized (this.singletonObjects) {// 双重检查singletonObject = this.singletonObjects.get(beanName);if (singletonObject == null) {singletonObject = this.earlySingletonObjects.get(beanName);if (singletonObject == null) {// 5. 从三级缓存获取Bean工厂ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);if (singletonFactory != null) {// 6. 通过工厂创建早期Bean引用singletonObject = singletonFactory.getObject();// 7. 放入二级缓存this.earlySingletonObjects.put(beanName, singletonObject);// 8. 从三级缓存移除this.singletonFactories.remove(beanName);}}}}}}return singletonObject;}// 添加单例Bean工厂到三级缓存protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {Assert.notNull(singletonFactory, "Singleton factory must not be null");synchronized (this.singletonObjects) {if (!this.singletonObjects.containsKey(beanName)) {// 放入三级缓存this.singletonFactories.put(beanName, singletonFactory);// 从二级缓存移除this.earlySingletonObjects.remove(beanName);}}}
}
为什么需要三级缓存?
// 问题:为什么不直接用二级缓存?
// 答案:为了处理AOP代理对象// 场景:如果Bean需要被AOP代理
@Service
public class UserService {@Autowiredprivate OrderService orderService;@Transactional // 需要AOP代理public void createUser() {orderService.createOrder();}
}// 三级缓存中存储的是ObjectFactory
addSingletonFactory("userService", () -> {// 如果需要AOP,返回代理对象// 如果不需要AOP,返回原始对象return getEarlyBeanReference(beanName, mbd, bean);
});// getEarlyBeanReference方法
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {Object exposedObject = bean;if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {for (BeanPostProcessor bp : getBeanPostProcessors()) {if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;// 如果是AOP代理,这里会返回代理对象exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);}}}return exposedObject;
}
完整的循环依赖解决流程(带AOP):
// 1. 创建UserService实例
UserService userService = new UserService(); // 原始对象// 2. 将ObjectFactory放入三级缓存(而不是直接放对象)
addSingletonFactory("userService", () -> getEarlyBeanReference("userService", mbd, userService));// 3. 属性注入时发现需要OrderService,去创建OrderService// 4. 创建OrderService实例
OrderService orderService = new OrderService();// 5. OrderService需要注入UserService,调用getSingleton("userService")
// 从三级缓存获取ObjectFactory,调用getObject()
// 如果UserService需要AOP,返回代理对象;否则返回原始对象
Object earlyUserService = singletonFactory.getObject();// 6. 将早期引用放入二级缓存,从三级缓存移除
earlySingletonObjects.put("userService", earlyUserService);
singletonFactories.remove("userService");// 7. OrderService注入完成,放入一级缓存// 8. UserService继续初始化,完成后也放入一级缓存
面试要点总结:
- 能解决的:单例Bean + Setter注入(字段注入、@Autowired)
- 不能解决的:构造器注入、原型Bean、多例Bean
- 核心机制:三级缓存 + 早期暴露
- 为什么三级:支持AOP代理,确保注入的是代理对象
- 检测时机:在Bean创建过程中,通过isSingletonCurrentlyInCreation判断
总结
Spring 框架是 Java 企业级开发的核心技术栈,掌握 Spring 的各个模块和特性对于 Java 开发者至关重要。本面试宝典涵盖了 Spring 的核心概念、IoC 容器、AOP、MVC、Boot、Security、Data、Cloud 等主要模块,以及事务管理、Bean 生命周期等高级特性。
重点掌握:
- IoC 和 DI 的原理和实现
- AOP 的切面编程思想
- Spring Boot 的自动配置机制
- 事务管理的使用和注意事项
- Bean 的生命周期和作用域
- 循环依赖的解决机制
通过深入理解这些概念和原理,能够在面试中展现出扎实的 Spring 技术功底。