Java Spring 面试题集
总题数: 71道 | 重点领域: IOC、AOP、MVC、事务管理 | 难度分布: 中级到高级
本文档整理了 Java Spring 框架的完整71道面试题目,涵盖核心容器、AOP、数据访问、Web开发等各个方面。
面试题目列表
1. 什么是循环依赖(常问)?
循环依赖是指两个或多个Bean之间相互依赖,形成一个闭环的依赖关系。最典型的情况是A依赖B,B又依赖A,形成循环依赖。
循环依赖的三种类型:
-
构造器循环依赖:
- A的构造方法中依赖B的实例,而B的构造方法中依赖A的实例
- 这种依赖无法被Spring解决,会导致
BeanCurrentlyInCreationException异常 - 因为基于构造器注入,对象需要在实例化时就注入所有依赖
-
Setter方法循环依赖:
- A的setter方法中依赖B的实例,而B的setter方法中依赖A的实例
- 这种依赖可以被Spring的三级缓存机制解决
- 单例作用域下,默认可以解决此类循环依赖
-
原型(Prototype)循环依赖:
- 原型作用域的Bean之间存在循环依赖
- Spring无法解决,会抛出
BeanCurrentlyInCreationException异常 - 因为原型Bean每次获取都会创建新实例,无法提前暴露创建中的对象
循环依赖示例代码:
1@Component2public class A {3 @Autowired4 private B b;5}67@Component8public class B {9 @Autowired10 private A a;11}为什么会有循环依赖问题:
- Spring IoC容器管理Bean生命周期,包括实例化和初始化
- 在依赖注入过程中,容器需要先创建所依赖的Bean
- 如果存在循环依赖,就会导致无法完成初始化过程
循环依赖的危害:
- 可能导致启动异常,应用无法正常运行
- 增加系统复杂性,不利于代码理解和维护
- 可能带来内存泄漏或资源释放问题
- 表明设计可能存在问题,违反了单一职责原则
解决循环依赖的方法:
- 重构设计,消除循环依赖
- 使用@Lazy注解延迟加载
- 使用setter注入替代构造器注入
- 使用@PostConstruct进行延迟初始化
- 使用AOP进行解耦
最佳实践:
- 应当尽量避免设计中的循环依赖
- 优先使用构造器注入,遇到循环依赖时再考虑setter注入
- 使用依赖倒置和接口分离原则优化设计
2. Spring 如何解决循环依赖?
Spring通过三级缓存机制解决单例作用域下的setter循环依赖问题。这三级缓存是:
1. 一级缓存(singletonObjects):
- 完全初始化好的单例对象的缓存
- 存放已经经历了完整生命周期、可以直接使用的Bean
- Map类型,key是Bean名称,value是完整Bean对象
2. 二级缓存(earlySingletonObjects):
- 早期的单例对象缓存,已实例化但尚未完全初始化(尚未填充属性)
- 存放原始Bean对象,用于解决循环依赖
- Map类型,key是Bean名称,value是早期Bean对象
3. 三级缓存(singletonFactories):
- 单例对象工厂的缓存
- 存放Bean工厂对象,主要用于生成早期的单例对象
- Map类型,key是Bean名称,value是对象工厂
循环依赖解决流程:
假设有循环依赖的Bean A和Bean B:
-
创建Bean A:
- 实例化Bean A,此时尚未填充属性
- 将A的对象工厂放入三级缓存(singletonFactories)
- ObjectFactory会返回A的早期引用,可能是原始对象,也可能是代理对象
-
填充Bean A的属性:
- 发现A依赖B,开始创建Bean B
-
创建Bean B:
- 实例化Bean B,此时尚未填充属性
- 将B的对象工厂放入三级缓存(singletonFactories)
-
填充Bean B的属性:
- 发现B依赖A
- 尝试从一级缓存获取A,未找到
- 尝试从二级缓存获取A,未找到
- 从三级缓存获取A的工厂,调用getObject()方法得到A的早期引用
- 将A从三级缓存移动到二级缓存
- B注入A的早期引用,完成B的初始化
- 将B放入一级缓存(singletonObjects),从二、三级缓存移除B
-
继续完成Bean A的创建:
- A注入已经完全初始化的B
- 完成A的初始化
- 将A放入一级缓存(singletonObjects),从二、三级缓存移除A
关键代码解析:
1// Spring容器解决循环依赖的核心代码(简化版)2protected Object getSingleton(String beanName, boolean allowEarlyReference) {3 // 从一级缓存获取4 Object singletonObject = this.singletonObjects.get(beanName);5 6 // 如果一级缓存没有,并且该Bean正在创建中7 if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {8 // 从二级缓存获取9 singletonObject = this.earlySingletonObjects.get(beanName);10 11 // 如果二级缓存没有,并且允许获取早期引用12 if (singletonObject == null && allowEarlyReference) {13 // 从三级缓存获取对象工厂14 ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);15 if (singletonFactory != null) {16 // 通过工厂创建早期引用17 singletonObject = singletonFactory.getObject();18 // 放入二级缓存,从三级缓存移除19 this.earlySingletonObjects.put(beanName, singletonObject);20 this.singletonFactories.remove(beanName);21 }22 }23 }24 return singletonObject;25}注意事项:
- 只能解决单例作用域的Bean循环依赖
- 不能解决构造器注入的循环依赖
- 不能解决@Depends-On循环依赖
- 不能解决多例(prototype)作用域的Bean循环依赖
- AOP可能会导致额外的复杂性,但Spring的三级缓存机制已经考虑到了这点
总结:Spring通过巧妙的三级缓存设计,成功解决了单例Bean之间的循环依赖问题,让开发者在大多数情况下无需关心这个问题。
3. 为什么 Spring 循环依赖需要三级缓存,二级不够吗?
Spring的循环依赖解决机制中,三级缓存看似复杂,但其实每个缓存都有其特定的用途。为了理解为什么需要三级缓存而不是二级缓存,我们需要分析各缓存的具体作用:
二级缓存不够的核心原因:AOP代理对象的处理
如果只有二级缓存(完成品、半成品),无法同时解决循环依赖和AOP代理的问题。三级缓存的关键在于延迟代理对象的创建。
详细分析:
-
一级缓存(singletonObjects):
- 存储完全初始化好的Bean
- 任何时候获取Bean,首先从这里取
-
二级缓存(earlySingletonObjects):
- 存储早期曝光的Bean,即已实例化但未完成属性填充和初始化的对象
- 作用是在循环依赖中提前返回未完成的实例
-
三级缓存(singletonFactories):
- 存储Bean的工厂对象,这些工厂可以返回Bean的早期引用,也可以是其AOP代理
- 关键点:工厂可以动态决定返回原始Bean还是其代理对象
为何需要三级缓存:
-
AOP代理的时机问题:
- Spring的设计原则是在Bean完全初始化后才创建其AOP代理
- 但循环依赖要求提前曝光Bean的引用
- 如果直接把原始Bean放入二级缓存,后续再创建代理,会导致有些地方注入的是原始Bean,有些地方注入的是代理Bean
-
提供统一的对象视图:
- 三级缓存中的工厂模式确保无论是否存在循环依赖,获取到的都是同一个对象(原始对象或其代理)
-
延迟代理创建:
- 只有真正发生循环依赖时,才会触发提前创建代理
- 如果没有循环依赖,则按照正常流程在Bean完全初始化后创建代理
对比二级缓存方案:
如果只有两级缓存:
- 要么在实例化后就立即创建代理放入二级缓存(可能导致不必要的代理创建)
- 要么在出现循环引用时才使用原始对象(导致同一Bean的不同引用可能指向不同对象)
关键代码示例:
1// 三级缓存中的工厂创建逻辑(简化)2addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));34// 获取早期引用方法5protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {6 Object exposedObject = bean;7 if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {8 for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) {9 // 如果需要,这里会创建AOP代理10 exposedObject = bp.getEarlyBeanReference(exposedObject, beanName);11 }12 }13 return exposedObject;14}场景说明: 假设Bean A和Bean B循环依赖,且Bean A需要被AOP代理:
-
使用三级缓存:
- 创建A的原始对象,放入三级缓存的工厂
- 填充A的属性,发现依赖B
- 创建B,填充B的属性,发现依赖A
- 通过三级缓存的工厂获取A的引用(如果需要,创建A的代理)
- 完成B的创建,注入到A
- 完成A的创建
- 结果:B注入的是A的代理对象,容器获取的A也是同一个代理对象
-
如果只有二级缓存:
- 创建A的原始对象,放入二级缓存
- B直接引用了这个原始对象
- A完成创建后,创建了代理对象
- 结果:B持有的是A的原始对象,而容器中保存的是A的代理对象,不一致
总结: 三级缓存的核心价值在于保证所有引用都指向同一个对象(原始对象或其代理),解决了循环依赖和AOP代理的矛盾。这是Spring设计的精妙之处,通过ObjectFactory的延迟执行特性,确保了对象一致性。
4. 看过源码吗?说下 Spring 由哪些重要的模块组成?
Spring框架是一个模块化设计的生态系统,由多个功能明确的模块组成。作为一个经常阅读Spring源码的开发者,我可以详细说明其核心模块结构:
1. 核心容器(Core Container):
-
spring-core:提供框架的基础功能,包含IoC和DI的核心实现
- 核心工具类、资源加载机制、类型转换系统
- 源码核心类:
Resource,BeanUtils,ReflectionUtils
-
spring-beans:Bean工厂相关实现,包含Bean的定义、创建和管理
- BeanFactory接口及实现
- Bean生命周期管理、依赖解析
- 源码核心类:
BeanFactory,DefaultListableBeanFactory,BeanDefinition
-
spring-context:构建于core和beans模块之上,提供上下文框架
- ApplicationContext接口及实现
- 国际化、事件传播、资源加载、环境抽象
- 源码核心类:
ApplicationContext,ClassPathXmlApplicationContext
-
spring-expression(SpEL):提供强大的表达式语言支持
- 运行时查询和操作对象图
- 源码核心类:
Expression,ExpressionParser
2. AOP和设备支持(AOP and Instrumentation):
-
spring-aop:面向切面编程的实现
- 基于JDK动态代理和CGLIB的AOP实现
- 源码核心类:
Advisor,Advice,Pointcut,AopProxy
-
spring-aspects:与AspectJ的集成
- 提供对AspectJ的支持
- 源码核心类:
AspectJExpressionPointcut
-
spring-instrument:提供类植入支持和类加载器实现
- 主要用于应用服务器
- 源码核心类:
InstrumentationSavingAgent
3. 数据访问/集成(Data Access/Integration):
-
spring-jdbc:JDBC抽象层
- 简化JDBC操作,处理异常
- JdbcTemplate模板类
- 源码核心类:
JdbcTemplate,DataSource
-
spring-tx:事务管理
- 声明式和编程式事务
- 事务同步管理
- 源码核心类:
PlatformTransactionManager,TransactionTemplate
-
spring-orm:ORM框架集成
- 与Hibernate、JPA、iBatis等集成
- 源码核心类:
HibernateTemplate,JpaTransactionManager
-
spring-oxm:Object/XML映射
- 提供抽象层简化XML映射
- 源码核心类:
Marshaller,Unmarshaller
-
spring-jms:JMS消息集成
- 发送和接收消息的支持
- 源码核心类:
JmsTemplate,MessageListener
4. Web:
-
spring-web:Web开发基础支持
- 文件上传、REST客户端、远程调用
- 源码核心类:
WebApplicationContext,MultipartResolver
-
spring-webmvc:Web MVC框架实现
- MVC框架、视图解析、Controller映射
- 源码核心类:
DispatcherServlet,Controller,ViewResolver
-
spring-websocket:WebSocket支持
- 提供WebSocket和SockJS实现
- 源码核心类:
WebSocketHandler,SockJsService
-
spring-webflux:响应式Web框架
- 非阻塞响应式应用支持
- 源码核心类:
WebClient,RouterFunction
5. 测试(Test):
- spring-test:测试模块
- Spring应用的单元测试和集成测试支持
- 源码核心类:
SpringJUnitConfig,MockMvc
模块依赖关系:
1┌─────────────────┐2 │ spring-test │3 └─────────────────┘4 ▲5 │6┌────────────┐ ┌───────────┐ │ ┌───────────┐7│ spring-aop ├────►│spring-beans├────►│ │spring-jdbc│8└────────────┘ └─────┬─────┘ │ └─────┬─────┘9 │ │ │10 ▼ │ ▼11 ┌──────────┐ │ ┌──────────┐12 │spring-core│◄─────┼─────┤spring-tx │13 └──────────┘ │ └──────────┘14 ▲ │15 │ │16 ┌───────────────────┐ │ ┌─────────────┐17 │ spring-context │◄┼──┤spring-orm │18 └─────────┬─────────┘ │ └─────────────┘19 │ │20 ▼ │21 ┌──────────────┐ │22 │spring-webmvc │◄──┘23 └──────────────┘Spring源码组织结构:
- 源码按功能模块组织,每个模块一个独立的Maven/Gradle项目
- 核心功能在spring-core、spring-beans、spring-context中实现
- 各模块有明确的职责边界,相互协作完成功能
Spring框架扩展点:
- BeanPostProcessor:Bean实例化和初始化的扩展
- BeanFactoryPostProcessor:BeanFactory配置的扩展
- ApplicationListener:事件监听机制
- Aware接口族:获取容器资源
- InitializingBean/DisposableBean:Bean生命周期控制
总结:Spring框架通过模块化设计实现了高内聚、低耦合的架构,使开发者可以只引入需要的模块而不必加载整个框架,同时提供了丰富的扩展点以满足定制化需求。
5. 什么是 Spring IOC?
IoC (Inversion of Control,控制反转) 是Spring框架最核心的设计原则,它将传统Java开发中的依赖管理方式颠倒过来,将对象的创建和依赖关系的维护从代码中移到外部容器。
传统依赖管理 vs IoC:
传统方式:
1public class UserService {2 // 主动创建依赖对象3 private UserDao userDao = new UserDaoImpl();4 5 public User getUser(String id) {6 return userDao.findById(id);7 }8}IoC方式:
1public class UserService {2 // 不再主动创建,由容器注入3 @Autowired4 private UserDao userDao;5 6 public User getUser(String id) {7 return userDao.findById(id);8 }9}IoC的核心思想:
- 控制:指对象的创建、管理和销毁等生命周期的控制权
- 反转:将控制权从应用代码转移到外部容器
- 依赖注入(DI):IoC的一种实现方式,容器主动将依赖注入到组件中
Spring IoC容器的功能:
- Bean的实例化:创建对象实例
- Bean的装配:注入依赖关系
- Bean的生命周期管理:初始化和销毁
- Bean的作用域控制:单例、原型等
- Bean的延迟加载:按需实例化
Spring IoC容器的实现:
- BeanFactory:基础IoC容器,提供基本的DI支持
- ApplicationContext:高级IoC容器,扩展了BeanFactory,提供更多企业级功能
IoC容器初始化流程:
- 读取配置:XML、注解或Java配置类
- 解析配置:转化为BeanDefinition
- 注册BeanDefinition:存入BeanDefinitionRegistry
- 实例化非延迟Bean:创建单例Bean
- 初始化Bean:属性注入、执行初始化方法等
依赖注入的方式:
-
构造器注入:通过构造方法注入依赖
java1@Component2public class UserService {3 private final UserDao userDao;45 // 构造器注入6 @Autowired7 public UserService(UserDao userDao) {8 this.userDao = userDao;9 }10} -
Setter注入:通过setter方法注入依赖
java1@Component2public class UserService {3 private UserDao userDao;45 // setter注入6 @Autowired7 public void setUserDao(UserDao userDao) {8 this.userDao = userDao;9 }10} -
字段注入:直接在字段上注入依赖
java1@Component2public class UserService {3 // 字段注入4 @Autowired5 private UserDao userDao;6}
IoC容器的核心组件:
- BeanDefinition:Bean的定义信息,包括类名、作用域、依赖等
- BeanDefinitionReader:读取并解析配置信息
- BeanFactory:Bean的工厂,负责创建和管理Bean
- BeanPostProcessor:Bean的后处理器,在Bean创建过程中介入处理
IoC的优势:
- 松耦合:组件之间的依赖关系由容器管理,降低耦合
- 可测试性:可以方便地模拟依赖,进行单元测试
- 模块化:各组件关注自身业务逻辑,依赖交由容器管理
- 灵活配置:可以通过配置改变组件间的依赖关系,无需修改代码
- 生命周期管理:统一管理对象的创建和销毁
IoC的实际应用示例:
1// 配置类2@Configuration3public class AppConfig {4 @Bean5 public DataSource dataSource() {6 DruidDataSource dataSource = new DruidDataSource();7 dataSource.setUrl("jdbc:mysql://localhost:3306/test");8 dataSource.setUsername("root");9 dataSource.setPassword("password");10 return dataSource;11 }12 13 @Bean14 public JdbcTemplate jdbcTemplate(DataSource dataSource) {15 return new JdbcTemplate(dataSource);16 }17}1819// 使用IoC容器20public class Application {21 public static void main(String[] args) {22 ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);23 JdbcTemplate jdbcTemplate = context.getBean(JdbcTemplate.class);24 // 使用jdbcTemplate25 }26}总结: Spring IoC通过控制反转和依赖注入,实现了对象创建和依赖关系的解耦,使应用更加模块化、可测试和可维护。它是Spring框架的核心特性,为其他功能模块如AOP、事务管理等提供了基础支持。
6. Spring IOC 有什么好处?
**IOC(Inversion of Control,控制反转)**是Spring框架的核心设计原则,通过反转对象创建和依赖管理的控制权,为应用程序带来了诸多好处:
1. 松耦合(Loose Coupling):
- 降低组件间依赖:组件不需要知道依赖的具体实现,只需了解接口
- 提高代码复用性:组件可以独立使用和测试,更容易在不同场景中复用
- 简化系统重构:可以轻松替换实现类,而不影响调用方
1// 不使用IOC2public class UserService {3 // 紧耦合,直接依赖实现类4 private UserDaoImpl userDao = new UserDaoImpl();5}67// 使用IOC8public class UserService {9 // 松耦合,依赖接口10 @Autowired11 private UserDao userDao; // 具体实现由Spring容器注入12}2. 更好的可测试性:
- 单元测试更简单:可以轻松模拟依赖对象(Mock)
- 测试替代实现:可以在测试环境使用特殊实现
- 无需初始化完整依赖链:测试时只关注被测组件
1// 测试更容易2@Test3public void testUserService() {4 // 创建测试替代品(模拟对象)5 UserDao mockDao = Mockito.mock(UserDao.class);6 when(mockDao.findById(1)).thenReturn(new User(1, "Test User"));7 8 // 手动注入模拟对象9 UserService service = new UserService();10 ReflectionTestUtils.setField(service, "userDao", mockDao);11 12 // 测试业务方法13 User user = service.getUserById(1);14 assertEquals("Test User", user.getName());15}3. 关注点分离(Separation of Concerns):
- 专注业务逻辑:开发者只需关注组件的核心功能
- 基础设施交给框架:对象生命周期、依赖管理等由Spring处理
- 声明式编程:通过注解或XML配置声明依赖,而非硬编码
4. 更好的可维护性:
- 代码更清晰:依赖关系显式声明,易于理解
- 标准化组件管理:统一的依赖注入方式
- 集中式配置:依赖关系在配置文件或注解中集中管理
5. 生命周期管理:
- 统一管理对象创建和销毁:Spring容器控制Bean的完整生命周期
- 延迟初始化:支持Bean的懒加载,优化资源使用
- 作用域控制:灵活控制Bean的作用域(单例、原型、请求、会话等)
6. AOP支持:
- 横切关注点:通过IOC容器更容易实现AOP功能
- 无侵入增强:不修改业务代码即可添加日志、事务等功能
- 可插拔服务:可以动态启用或禁用某些横切功能
7. 提高开发效率:
- 减少样板代码:无需手动创建对象和管理依赖
- 开箱即用的集成:与各种技术栈的预配置集成
- 丰富的生态系统:大量现成的组件可以直接注入使用
8. 企业级功能支持:
- 声明式事务:基于IOC和AOP实现的事务管理
- 安全框架集成:与Spring Security等无缝协作
- 缓存抽象:统一的缓存访问机制
9. 模块化应用设计:
- 组件化开发:鼓励将应用拆分为独立组件
- 按需装配:只加载应用需要的组件
- 清晰的模块边界:通过依赖注入定义模块接口
10. 设计模式的实现:
- 工厂模式:BeanFactory和ApplicationContext
- 单例模式:默认的Bean作用域
- 代理模式:AOP实现
- 观察者模式:事件监听机制
Spring IOC使用示例:
XML配置方式:
1<!-- applicationContext.xml -->2<beans>3 <bean id="userDao" class="com.example.UserDaoImpl" />4 5 <bean id="userService" class="com.example.UserServiceImpl">6 <property name="userDao" ref="userDao" />7 </bean>8</beans>注解配置方式:
1@Repository2public class UserDaoImpl implements UserDao {3 // 实现省略4}56@Service7public class UserServiceImpl implements UserService {8 private final UserDao userDao;9 10 @Autowired11 public UserServiceImpl(UserDao userDao) {12 this.userDao = userDao;13 }14}Java配置方式:
1@Configuration2public class AppConfig {3 @Bean4 public UserDao userDao() {5 return new UserDaoImpl();6 }7 8 @Bean9 public UserService userService(UserDao userDao) {10 return new UserServiceImpl(userDao);11 }12}7. Spring 中的 DI 是什么?
**DI(Dependency Injection,依赖注入)**是实现IoC的主要方式,是Spring框架中一种用于管理对象依赖关系的核心机制。通过DI,对象的依赖不再由对象自身创建,而是由外部容器注入,从而实现了"控制反转"。
1. DI的基本概念:
- 依赖:当一个类A使用另一个类B的功能时,我们说A依赖于B
- 注入:向类中提供其所依赖的对象的过程
- 控制反转:依赖对象的控制权由类转移到外部容器
2. Spring支持的主要依赖注入方式:
构造器注入(Constructor Injection):
- 通过构造函数参数注入依赖
- 依赖在对象创建时提供
- 可以确保依赖不为null
- 适合强制性依赖
1@Service2public class UserServiceImpl implements UserService {3 private final UserDao userDao;4 private final EmailService emailService;5 6 // 构造器注入7 @Autowired8 public UserServiceImpl(UserDao userDao, EmailService emailService) {9 this.userDao = userDao;10 this.emailService = emailService;11 }12}XML配置:
1<bean id="userService" class="com.example.UserServiceImpl">2 <constructor-arg ref="userDao" />3 <constructor-arg ref="emailService" />4</bean>Setter注入(Setter Injection):
- 通过setter方法注入依赖
- 对象创建后注入依赖
- 允许可选依赖
- 支持后期重新注入
1@Service2public class UserServiceImpl implements UserService {3 private UserDao userDao;4 private EmailService emailService;5 6 // setter注入7 @Autowired8 public void setUserDao(UserDao userDao) {9 this.userDao = userDao;10 }11 12 @Autowired13 public void setEmailService(EmailService emailService) {14 this.emailService = emailService;15 }16}XML配置:
1<bean id="userService" class="com.example.UserServiceImpl">2 <property name="userDao" ref="userDao" />3 <property name="emailService" ref="emailService" />4</bean>字段注入(Field Injection):
- 直接在类的字段上注入依赖
- 代码最简洁
- 但不推荐用于生产环境(难以测试、隐藏依赖)
1@Service2public class UserServiceImpl implements UserService {3 // 字段注入4 @Autowired5 private UserDao userDao;6 7 @Autowired8 private EmailService emailService;9}3. 接口注入:
- 实现特定的注入接口
- Spring中较少使用这种方式
- 通常用于特定框架集成
4. 依赖注入的类型:
基于值的注入:
- 注入基本类型、字符串等值
- 使用@Value注解或XML中的value属性
1@Service2public class EmailService {3 @Value("${mail.from}")4 private String fromAddress;5 6 @Value("${mail.reply-to}")7 private String replyTo;8}基于Bean的注入:
- 注入其他Bean引用
- 使用@Autowired、@Resource或XML中的ref属性
集合注入:
- 注入数组、列表、集合、映射等
- 在XML中使用列表、集合和映射标签
1<bean id="courseService" class="com.example.CourseService">2 <property name="courseNames">3 <list>4 <value>Java Programming</value>5 <value>Spring Framework</value>6 <value>Database Design</value>7 </list>8 </property>9</bean>5. 自动装配(Autowiring)模式:
- no:不进行自动装配,需要显式指定依赖
- byName:根据属性名称自动装配
- byType:根据属性类型自动装配(若有多个匹配类型的Bean,会抛出异常)
- constructor:类似byType,但应用于构造器参数
- autodetect:先尝试constructor,再尝试byType(已过时)
6. 注解驱动的依赖注入:
- @Autowired:Spring提供的注解,默认按类型装配,可与@Qualifier结合使用指定名称
- @Inject:JSR-330标准注解,功能类似@Autowired
- @Resource:JSR-250标准注解,默认按名称装配,其次按类型
- @Value:注入简单值,支持SpEL表达式
- @Qualifier:细化自动装配的匹配规则
7. DI的最佳实践:
-
优先使用构造器注入:
- 保证依赖不可变
- 确保必要依赖在对象创建时可用
- 防止NullPointerException
- 有助于识别构造器参数过多的情况(可能需要重构)
-
为接口而非实现注入:
- 降低耦合度
- 便于更换实现
- 方便测试(可以注入模拟对象)
-
避免字段注入:
- 隐藏依赖关系
- 难以进行单元测试
- 与容器紧密耦合
- 无法创建不可变对象
-
使用合适的作用域:
- 默认单例适合无状态服务
- 原型适合有状态对象
- 请求/会话作用域适合web应用
8. DI的优势:
- 解耦组件:减少组件间的直接依赖
- 提高可测试性:便于使用模拟对象进行单元测试
- 灵活配置:可以在不修改代码的情况下改变依赖关系
- 支持热插拔:可在运行时替换组件实现
- 更专注业务逻辑:减少基础设施代码
9. 注意事项:
- 循环依赖:构造器注入无法解决循环依赖问题,Setter注入可以
- 加载顺序:需要正确管理Bean的初始化顺序
- 依赖过多:过多的依赖可能表明类的责任过大,违反单一责任原则
- 异常处理:注意处理依赖注入可能引发的异常,如NoSuchBeanDefinitionException
8. 什么是 Spring Bean?
Spring Bean是Spring框架IoC容器管理的对象,是构成Spring应用程序的基础构建块。这些Bean由Spring容器负责实例化、配置、装配并管理其完整生命周期。
1. Bean的定义:
Bean是在Spring应用中由IoC容器实例化、组装和管理的Java对象。与普通Java对象的区别在于:
- 由Spring容器创建和管理
- 遵循特定的生命周期
- 可以通过依赖注入获取其他Bean
- 具有可配置的作用域、延迟加载等特性
2. Bean的配置方式:
XML配置:
1<bean id="userService" class="com.example.service.UserServiceImpl">2 <property name="userDao" ref="userDao"/>3 <property name="maxRetries" value="3"/>4</bean>注解配置:
1@Component // 或@Service, @Repository, @Controller等2public class UserServiceImpl implements UserService {3 // Bean实现4}Java配置:
1@Configuration2public class AppConfig {3 @Bean4 public UserService userService() {5 UserServiceImpl service = new UserServiceImpl();6 service.setMaxRetries(3);7 return service;8 }9}自动扫描配置:
1<context:component-scan base-package="com.example"/>或
1@Configuration2@ComponentScan("com.example")3public class AppConfig {4 // 配置5}3. Bean的命名:
- ID:Bean的唯一标识符
- Name:Bean的别名,可以有多个
- 未指定名称时,默认使用类名首字母小写
1<!-- 带ID和Name的Bean定义 -->2<bean id="userService" name="userSrv,userManager" class="com.example.UserServiceImpl"/>4. Bean的作用域(Scope):
Spring提供了多种Bean作用域,用于控制Bean实例的生命周期和可见性:
- singleton(默认):整个应用中只创建一个实例
- prototype:每次请求都创建新实例
- request:每个HTTP请求创建一个实例(仅Web环境)
- session:每个HTTP会话创建一个实例(仅Web环境)
- application:每个ServletContext创建一个实例(仅Web环境)
- websocket:每个WebSocket创建一个实例(仅Web环境)
- 自定义作用域:实现Scope接口创建自定义作用域
1@Component2@Scope("prototype")3public class ShoppingCart {4 // 购物车实现5}5. Bean的生命周期:
Spring Bean的生命周期包括多个阶段,每个阶段都可以进行自定义处理:
初始化阶段:
- 实例化Bean
- 设置Bean属性
- BeanNameAware和BeanFactoryAware回调
- ApplicationContextAware回调(如果实现了该接口)
- BeanPostProcessor的预处理方法
- InitializingBean的afterPropertiesSet方法
- 自定义init-method方法
- BeanPostProcessor的后处理方法
销毁阶段:
- DisposableBean的destroy方法
- 自定义destroy-method方法
生命周期控制方式:
接口方式:
1public class UserService implements InitializingBean, DisposableBean {2 @Override3 public void afterPropertiesSet() throws Exception {4 // 初始化逻辑5 }6 7 @Override8 public void destroy() throws Exception {9 // 销毁逻辑10 }11}注解方式:
1@Component2public class UserService {3 @PostConstruct4 public void init() {5 // 初始化逻辑6 }7 8 @PreDestroy9 public void cleanup() {10 // 销毁逻辑11 }12}XML方式:
1<bean id="userService" class="com.example.UserService" 2 init-method="init" destroy-method="cleanup"/>6. Bean的延迟初始化(Lazy Initialization):
默认情况下,单例Bean在容器启动时就会被实例化。可以通过延迟初始化改变这一行为:
1@Component2@Lazy3public class ExpensiveService {4 // 此Bean将在首次使用时才被实例化5}1<bean id="expensiveService" class="com.example.ExpensiveService" lazy-init="true"/>7. Bean的依赖注入:
Spring支持多种依赖注入方式,使Bean可以获取其所需的依赖:
- 构造器注入
- Setter方法注入
- 字段注入
- 接口注入
8. Bean的自动装配(Autowiring):
Spring可以自动装配Bean之间的依赖关系,减少配置工作:
1@Component2public class UserService {3 private final UserRepository repository;4 5 @Autowired // 自动装配UserRepository类型的Bean6 public UserService(UserRepository repository) {7 this.repository = repository;8 }9}9. Bean的继承:
Bean定义可以继承其他Bean的配置,形成父子关系:
1<bean id="baseService" abstract="true">2 <property name="timeout" value="30000"/>3 <property name="maxRetries" value="3"/>4</bean>56<bean id="userService" class="com.example.UserService" parent="baseService">7 <property name="userDao" ref="userDao"/>8</bean>10. 特殊类型的Bean:
- FactoryBean:用于创建复杂Bean的工厂
- BeanPostProcessor:用于Bean初始化前后的处理
- BeanFactoryPostProcessor:用于BeanFactory配置的修改
11. Bean的实例化方式:
- 构造函数实例化:最常用的方式
- 静态工厂方法:通过调用静态工厂方法创建Bean
- 实例工厂方法:通过调用另一个Bean的实例方法创建Bean
12. Bean的元数据:
Spring Bean可以包含丰富的元数据,用于描述其特性和行为:
- 依赖描述
- 初始化和销毁方法
- 作用域信息
- 懒加载标志
- 自动装配模式
- 依赖检查设置
9. Spring 中的 BeanFactory 是什么?
BeanFactory是Spring框架中最基础、最核心的IoC容器接口,负责实例化、配置和管理Bean。它是Spring容器功能的最基本抽象,定义了访问和管理Bean的基本方法,是Spring IoC容器系列的根接口。
1. BeanFactory的核心功能:
- Bean的实例化与管理:创建、获取和管理Bean实例
- 依赖注入:为Bean注入依赖
- Bean生命周期管理:控制Bean的初始化和销毁
- 作用域管理:维护不同作用域的Bean实例
- 配置元数据处理:读取和解析Bean定义
2. BeanFactory接口定义:
BeanFactory接口定义了多个用于访问和管理Bean的方法:
1public interface BeanFactory {2 // 根据名称获取Bean3 Object getBean(String name) throws BeansException;4 5 // 根据名称和类型获取Bean6 <T> T getBean(String name, Class<T> requiredType) throws BeansException;7 8 // 根据类型获取Bean9 <T> T getBean(Class<T> requiredType) throws BeansException;10 11 // 检查是否包含指定名称的Bean12 boolean containsBean(String name);13 14 // 检查指定名称的Bean是否为单例15 boolean isSingleton(String name) throws NoSuchBeanDefinitionException;16 17 // 检查指定名称的Bean是否为原型18 boolean isPrototype(String name) throws NoSuchBeanDefinitionException;19 20 // 其他方法...21}3. BeanFactory的主要实现类:
- XmlBeanFactory(已过时):基于XML配置文件的BeanFactory实现
- DefaultListableBeanFactory:最常用的BeanFactory实现,是许多IoC容器的基础
- ConfigurableBeanFactory:可配置的BeanFactory接口,支持单例和原型
- HierarchicalBeanFactory:具有层次结构的BeanFactory接口,支持父子容器
- AutowireCapableBeanFactory:支持自动装配的BeanFactory接口
- ListableBeanFactory:支持枚举Bean的BeanFactory接口
4. BeanFactory的使用示例:
1// 创建BeanFactory实例2DefaultListableBeanFactory factory = new DefaultListableBeanFactory();34// 配置Bean定义加载器5XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);6reader.loadBeanDefinitions(new ClassPathResource("applicationContext.xml"));78// 获取Bean实例9UserService userService = factory.getBean("userService", UserService.class);1011// 使用Bean12userService.createUser("admin", "password");5. BeanFactory与ApplicationContext的区别:
BeanFactory是Spring容器的基础接口,而ApplicationContext是其高级实现,提供了更多企业级功能:
| 特性 | BeanFactory | ApplicationContext |
|---|---|---|
| Bean实例化时机 | 延迟加载(第一次获取时) | 启动时实例化(可配置) |
| 国际化支持 | 不支持 | 支持 |
| 事件发布 | 不支持 | 支持 |
| AOP集成 | 需手动配置 | 默认集成 |
| 资源访问 | 有限支持 | 更丰富的资源加载能力 |
| 数据验证 | 不支持 | 支持 |
| Web支持 | 无 | 有专门的Web实现 |
6. BeanFactory懒加载机制:
BeanFactory采用延迟加载策略,只有在第一次请求Bean时才会实例化它:
- 减少启动时的资源消耗
- 适合资源受限的环境
- 但可能导致问题延迟暴露
7. BeanFactory的加载过程:
- 加载配置元数据:从XML、注解或Java配置加载Bean定义
- 解析Bean定义:将配置信息转换为BeanDefinition对象
- 注册Bean定义:在BeanDefinitionRegistry中注册BeanDefinition
- 后处理Bean定义:应用BeanFactoryPostProcessor进行定义修改
- 实例化Bean:在请求时创建Bean实例
- 依赖注入:为Bean注入依赖
- 应用Bean后处理器:使用BeanPostProcessor处理Bean
- 初始化Bean:调用初始化方法
8. BeanFactory的扩展点:
- BeanFactoryPostProcessor:允许在实例化Bean前修改Bean定义
- BeanPostProcessor:允许在Bean初始化前后对其进行处理
- FactoryBean:自定义Bean的创建逻辑
9. BeanFactory中的Bean作用域:
- singleton:单例作用域,每个容器只创建一个实例
- prototype:原型作用域,每次请求都创建新实例
10. BeanFactory的层次结构:
BeanFactory支持父子层次结构:
- 子BeanFactory可以访问父BeanFactory中的Bean
- 但父BeanFactory无法访问子BeanFactory中的Bean
- 适合构建模块化应用
1// 创建父BeanFactory2DefaultListableBeanFactory parentFactory = new DefaultListableBeanFactory();3// 配置父BeanFactory...45// 创建子BeanFactory6DefaultListableBeanFactory childFactory = new DefaultListableBeanFactory(parentFactory);7// 配置子BeanFactory...89// 子BeanFactory可以访问父BeanFactory中的Bean10Object parentBean = childFactory.getBean("parentBean");11. 主要使用场景:
- 资源受限环境:嵌入式系统、移动应用等
- 需要精细控制Bean加载时机:延迟初始化重要的场景
- 自定义容器扩展:构建自定义容器实现
- 特殊部署场景:非标准化的部署环境
10. Spring 中的 FactoryBean 是什么?
FactoryBean是Spring框架中的一种特殊Bean,它是一个工厂Bean,用于生产其他Bean实例。与普通Bean不同,当从容器获取FactoryBean时,容器返回的不是FactoryBean本身,而是它创建的Bean实例。这种机制为复杂Bean的创建提供了灵活的方法,特别是当Bean的创建过程需要复杂逻辑时。
1. FactoryBean接口定义:
1public interface FactoryBean<T> {2 // 返回此工厂创建的对象实例3 T getObject() throws Exception;4 5 // 返回此工厂创建的对象的类型6 Class<?> getObjectType();7 8 // 返回由FactoryBean创建的对象是否是单例9 default boolean isSingleton() {10 return true;11 }12}2. FactoryBean的工作原理:
- 当容器处理实现了FactoryBean接口的Bean时,它会调用getObject()方法获取实际的Bean
- 默认情况下,容器返回FactoryBean生成的对象实例
- 如果想要获取FactoryBean本身,需要在Bean名称前加上"&"前缀
1// 获取FactoryBean创建的Bean2Object bean = context.getBean("myBean");34// 获取FactoryBean本身5FactoryBean factoryBean = (FactoryBean) context.getBean("&myBean");3. FactoryBean的实现示例:
1@Component("myDataSource")2public class DataSourceFactoryBean implements FactoryBean<DataSource> {3 private String url;4 private String username;5 private String password;6 7 // setter方法省略...8 9 @Override10 public DataSource getObject() throws Exception {11 BasicDataSource dataSource = new BasicDataSource();12 dataSource.setDriverClassName("com.mysql.jdbc.Driver");13 dataSource.setUrl(url);14 dataSource.setUsername(username);15 dataSource.setPassword(password);16 dataSource.setMaxActive(100);17 dataSource.setMaxIdle(30);18 dataSource.setMaxWait(10000);19 return dataSource;20 }21 22 @Override23 public Class<?> getObjectType() {24 return DataSource.class;25 }26 27 @Override28 public boolean isSingleton() {29 return true; // 返回单例数据源30 }31}4. FactoryBean的应用场景:
复杂对象创建:
- 创建需要复杂初始化的对象
- 对象创建需要运行时信息
- 需要进行条件逻辑判断来决定返回的对象类型
代理对象创建:
- 创建AOP代理
- 动态代理的应用
- RMI代理对象
第三方库集成:
- 整合不是按Spring期望方式配置的第三方组件
- 封装复杂的第三方API初始化逻辑
延迟加载或按需加载:
- 对象创建开销大,需要延迟初始化
- 条件性地创建对象
5. Spring中常用的FactoryBean实现:
- ProxyFactoryBean:创建AOP代理
- JndiObjectFactoryBean:通过JNDI查找获取对象
- LocalSessionFactoryBean:创建Hibernate SessionFactory
- SqlSessionFactoryBean:创建MyBatis SqlSessionFactory
- RestTemplateBuilder:构建RestTemplate实例
6. FactoryBean和BeanFactory的区别:
| 特性 | FactoryBean | BeanFactory |
|---|---|---|
| 类型 | 接口,由开发者实现 | 接口,由Spring框架实现 |
| 作用 | 创建特定的Bean实例 | Spring IoC容器的根接口 |
| 使用场景 | 复杂Bean的创建 | 管理和获取所有Bean |
| 使用方式 | 被Spring容器调用 | 作为Spring容器使用 |
| 获取方法 | getObject() | getBean() |
| 实例数量 | 一个FactoryBean对应一种Bean | 管理多种Bean |
7. FactoryBean的生命周期:
FactoryBean本身也是容器管理的Bean,因此遵循标准的Bean生命周期:
- 实例化
- 依赖注入
- 初始化
- 销毁
而FactoryBean创建的Bean可以有独立的生命周期管理。
8. FactoryBean与XML配置结合:
1<bean id="myDataSource" class="com.example.DataSourceFactoryBean">2 <property name="url" value="jdbc:mysql://localhost:3306/mydb"/>3 <property name="username" value="root"/>4 <property name="password" value="password"/>5</bean>67<!-- 使用FactoryBean创建的DataSource -->8<bean id="userDao" class="com.example.UserDaoImpl">9 <property name="dataSource" ref="myDataSource"/>10</bean>9. FactoryBean与Java配置结合:
1@Configuration2public class AppConfig {3 @Bean4 public DataSourceFactoryBean dataSourceFactoryBean() {5 DataSourceFactoryBean factoryBean = new DataSourceFactoryBean();6 factoryBean.setUrl("jdbc:mysql://localhost:3306/mydb");7 factoryBean.setUsername("root");8 factoryBean.setPassword("password");9 return factoryBean;10 }11 12 @Bean13 public UserDao userDao(DataSource dataSource) {14 UserDaoImpl userDao = new UserDaoImpl();15 userDao.setDataSource(dataSource);16 return userDao;17 }18}10. FactoryBean的高级特性:
- 懒加载支持:FactoryBean可以延迟创建目标Bean
- 依赖注入:FactoryBean可以使用Spring的依赖注入获取所需依赖
- 条件创建:可以基于环境或配置条件创建不同实例
- 生命周期回调:可以实现InitializingBean或使用@PostConstruct来初始化
11. 自定义SmartFactoryBean:
SmartFactoryBean是FactoryBean的扩展接口,增加了对早期初始化的控制:
1public interface SmartFactoryBean<T> extends FactoryBean<T> {2 // 控制是否需要在启动时就急切地初始化3 default boolean isEagerInit() {4 return false;5 }6}12. FactoryBean与普通Bean对比:
FactoryBean方式:
1// 定义FactoryBean2public class ComplexObjectFactoryBean implements FactoryBean<ComplexObject> {3 private String param1;4 private int param2;5 6 @Override7 public ComplexObject getObject() throws Exception {8 ComplexObject obj = new ComplexObject();9 // 复杂初始化逻辑...10 return obj;11 }12 13 @Override14 public Class<?> getObjectType() {15 return ComplexObject.class;16 }17}普通Bean方式:
1@Configuration2public class AppConfig {3 @Bean4 public ComplexObject complexObject() {5 ComplexObject obj = new ComplexObject();6 // 复杂初始化逻辑...7 return obj;8 }9}FactoryBean方式更适合需要封装复杂创建逻辑的场景,特别是当这些逻辑需要被多处复用时。
评论区 / Comments