Spring IoC与DI详解:从Bean概念到手写实现
一、Spring Bean的概念与本质
1.1 什么是Bean?
在Spring框架中,Bean是一个由Spring IoC容器实例化、组装和管理的对象。Bean及其之间的依赖关系通过容器使用的配置元数据来定义。简单来说,Bean就是Spring容器管理的Java对象。简单来说,在Spring里,Bean就是由Spring容器创建、管理和装配的对象,容器负责它的生命周期(创建、初始化、销毁)和依赖注入,开发者无需手动new,直接用就行。
Bean的核心特征:
- 由Spring容器创建和管理
- 遵循依赖注入原则
- 生命周期由容器控制
- 可以通过名称或类型检索
1.2 Bean与普通Java对象的区别
特性 | 普通Java对象 | Spring Bean |
---|---|---|
创建方式 | 通过new关键字创建 | 由Spring容器创建 |
依赖管理 | 手动处理依赖关系 | 容器自动处理依赖注入 |
生命周期 | 由程序员控制 | 由容器管理 |
作用域 | 通常是单例 | 支持多种作用域(singleton, prototype等) |
配置方式 | 硬编码在程序中 | 通过配置元数据定义 |
1.3 Bean的配置元数据
Spring支持三种配置方式定义Bean:
- XML配置文件:传统的配置方式
- 注解配置:基于Java注解
- Java配置类:基于Java代码
本文将重点介绍XML配置方式实现IoC和DI。
二、基于XML的IoC容器实现
2.1 基础环境搭建
首先创建一个Maven项目,添加Spring核心依赖:
<dependencies><!-- Spring核心容器 --><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.3.10</version></dependency>
</dependencies>
2.2 创建Spring XML配置文件
在resources目录下创建applicationContext.xml
:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttps://www.springframework.org/schema/beans/spring-beans.xsd"><!-- 最基本的Bean定义 --><bean id="simpleBean" class="com.example.SimpleBean"/></beans>
2.3 创建并获取Bean
public class MainApp {public static void main(String[] args) {// 1. 加载Spring配置文件,创建容器ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");// 2. 获取Bean实例SimpleBean bean = (SimpleBean) context.getBean("simpleBean");// 3. 使用Beanbean.doSomething();}
}
三、依赖注入(DI)的实现方式
3.1 构造器注入
UserService.java
public class UserService {private UserRepository userRepository;// 构造器public UserService(UserRepository userRepository) {this.userRepository = userRepository;}public void saveUser(User user) {userRepository.save(user);}
}
applicationContext.xml
<bean id="userRepository" class="com.example.UserRepositoryImpl"/><bean id="userService" class="com.example.UserService"><constructor-arg ref="userRepository"/>
</bean>
3.2 Setter方法注入
OrderService.java
public class OrderService {private OrderDao orderDao;// Setter方法public void setOrderDao(OrderDao orderDao) {this.orderDao = orderDao;}
}
applicationContext.xml
<bean id="orderDao" class="com.example.OrderDaoImpl"/><bean id="orderService" class="com.example.OrderService"><property name="orderDao" ref="orderDao"/>
</bean>
3.3 字段注入(不推荐)
虽然可以通过字段注入,但推荐使用构造器注入:
<bean id="productService" class="com.example.ProductService"><property name="productDao" ref="productDao"/>
</bean>
四、Bean的作用域与生命周期
4.1 Bean的作用域
在XML中通过scope属性定义:
<!-- 单例模式(默认) -->
<bean id="singletonBean" class="com.example.SingletonBean" scope="singleton"/><!-- 原型模式(每次获取新实例) -->
<bean id="prototypeBean" class="com.example.PrototypeBean" scope="prototype"/><!-- Web相关作用域 -->
<bean id="requestBean" class="com.example.RequestBean" scope="request"/>
<bean id="sessionBean" class="com.example.SessionBean" scope="session"/>
<bean id="applicationBean" class="com.example.ApplicationBean" scope="application"/>
4.2 Bean的生命周期回调
方式一:实现接口
public class LifecycleBean implements InitializingBean, DisposableBean {@Overridepublic void afterPropertiesSet() throws Exception {// 初始化逻辑}@Overridepublic void destroy() throws Exception {// 销毁逻辑}
}
方式二:XML配置
<bean id="lifecycleBean" class="com.example.LifecycleBean"init-method="customInit" destroy-method="customDestroy"/>
五、集合类型的依赖注入
5.1 List注入
<bean id="collectionBean" class="com.example.CollectionBean"><property name="nameList"><list><value>张三</value><value>李四</value><value>王五</value></list></property>
</bean>
5.2 Map注入
<bean id="collectionBean" class="com.example.CollectionBean"><property name="userMap"><map><entry key="admin" value-ref="adminUser"/><entry key="guest" value-ref="guestUser"/></map></property>
</bean>
5.3 Properties注入
<bean id="dataSource" class="com.example.BasicDataSource"><property name="properties"><props><prop key="driver">com.mysql.jdbc.Driver</prop><prop key="url">jdbc:mysql://localhost:3306/test</prop><prop key="username">root</prop><prop key="password">123456</prop></props></property>
</bean>
六、自动装配(autowiring)配置
6.1 XML中的自动装配模式
<!-- 1. byName: 根据属性名匹配Bean ID -->
<bean id="customerService" class="com.example.CustomerService" autowire="byName"/><!-- 2. byType: 根据属性类型匹配Bean -->
<bean id="orderService" class="com.example.OrderService" autowire="byType"/><!-- 3. constructor: 构造器参数自动装配 -->
<bean id="paymentService" class="com.example.PaymentService" autowire="constructor"/><!-- 4. default/no: 不自动装配(默认) -->
<bean id="productService" class="com.example.ProductService" autowire="default"/>
6.2 自动装配的限制与解决方案
问题1:多个同类型Bean
<bean id="userDaoMySQL" class="com.example.UserDaoMySQLImpl"/>
<bean id="userDaoOracle" class="com.example.UserDaoOracleImpl"/><!-- 解决方案1:使用primary -->
<bean id="userDaoMySQL" class="com.example.UserDaoMySQLImpl" primary="true"/><!-- 解决方案2:使用autowire-candidate -->
<bean id="userDaoOracle" class="com.example.UserDaoOracleImpl" autowire-candidate="false"/>
问题2:必须依赖
<!-- 设置required=false允许依赖为null -->
<bean id="optionalService" class="com.example.OptionalService" autowire="byType"><property name="required" value="false"/>
</bean>
七、基于XML的AOP配置
7.1 配置AOP命名空间
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/beanshttps://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/aophttps://www.springframework.org/schema/aop/spring-aop.xsd">
</beans>
7.2 定义切面
<!-- 1. 定义切面Bean -->
<bean id="loggingAspect" class="com.example.LoggingAspect"/><!-- 2. 配置AOP -->
<aop:config><!-- 定义切点 --><aop:pointcut id="serviceMethods" expression="execution(* com.example.service.*.*(..))"/><!-- 定义切面 --><aop:aspect ref="loggingAspect"><!-- 前置通知 --><aop:before pointcut-ref="serviceMethods" method="beforeAdvice"/><!-- 后置通知 --><aop:after-returning pointcut-ref="serviceMethods" method="afterReturningAdvice"returning="result"/></aop:aspect>
</aop:config>
八、Spring与第三方库集成
8.1 集成JDBC
<!-- 1. 配置数据源 -->
<bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource"><property name="driverClassName" value="com.mysql.jdbc.Driver"/><property name="url" value="jdbc:mysql://localhost:3306/test"/><property name="username" value="root"/><property name="password" value="123456"/>
</bean><!-- 2. 配置JdbcTemplate -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"><property name="dataSource" ref="dataSource"/>
</bean><!-- 3. 配置DAO -->
<bean id="userDao" class="com.example.UserDaoImpl"><property name="jdbcTemplate" ref="jdbcTemplate"/>
</bean>
8.2 集成Hibernate
<!-- 1. 配置SessionFactory -->
<bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean"><property name="dataSource" ref="dataSource"/><property name="hibernateProperties"><props><prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop><prop key="hibernate.show_sql">true</prop><prop key="hibernate.hbm2ddl.auto">update</prop></props></property><property name="packagesToScan" value="com.example.entity"/>
</bean><!-- 2. 配置事务管理器 -->
<bean id="transactionManager"class="org.springframework.orm.hibernate5.HibernateTransactionManager"><property name="sessionFactory" ref="sessionFactory"/>
</bean><!-- 3. 启用注解驱动的事务管理 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
九、Spring测试框架集成
9.1 配置测试环境
<!-- 测试相关依赖 -->
<dependency><groupId>org.springframework</groupId><artifactId>spring-test</artifactId><version>5.3.10</version><scope>test</scope>
</dependency>
<dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.13.2</version><scope>test</scope>
</dependency>
9.2 编写集成测试
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class UserServiceTest {@Autowiredprivate UserService userService;@Testpublic void testSaveUser() {User user = new User("test", "test@example.com");userService.saveUser(user);// 断言验证}
}
十、Spring XML配置代码案例
10.1 模块化配置
将大型配置文件拆分为多个小文件:
applicationContext.xml
<import resource="classpath:spring-datasource.xml"/>
<import resource="classpath:spring-service.xml"/>
<import resource="classpath:spring-dao.xml"/>
10.2 使用p命名空间简化配置
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:p="http://www.springframework.org/schema/p"xsi:schemaLocation="http://www.springframework.org/schema/beanshttps://www.springframework.org/schema/beans/spring-beans.xsd"><bean id="userService" class="com.example.UserService"p:userRepository-ref="userRepository"/>
</beans>
10.3 使用SpEL表达式
<bean id="systemSettings" class="com.example.SystemSettings"><property name="appName" value="#{systemProperties['app.name']}"/><property name="maxUsers" value="#{T(java.lang.Integer).parseInt(systemProperties['max.users'])}"/>
</bean>
结语
通过本文的详细讲解,我们全面了解了Spring IoC容器和依赖注入机制在XML配置方式下的实现。从最基本的Bean定义到复杂的AOP配置,从简单的属性注入到集合类型的处理,XML配置方式提供了强大的灵活性和明确的配置结构。虽然现代Spring开发更倾向于使用注解和Java配置,但理解XML配置对于维护遗留系统和深入理解Spring原理仍然非常重要。