在现代软件开发中,Spring框架以其强大的控制反转(IoC)和依赖注入(DI)特性而闻名。本文将深入探讨这两个概念,帮助你理解如何通过Spring实现松耦合,提升代码的灵活性和可维护性。
什么是控制反转(IoC)
控制反转(Inversion of Control,IoC)是Spring框架的核心思想之一。其核心目的是通过将对象的创建和管理交由Spring框架来实现对象之间的解耦关系。IoC的两大要素为:
- 控制:指的是对象创建和管理的权力。
- 反转:将创建和管理的控制权交给外部环境(如Spring IoC容器)。
硬编码解耦示例
在没有IoC之前,开发者通常需要硬编码来创建对象。下面是一个使用反射技术连接数据库的传统方式:
// 创建工具类
Properties properties = new Properties();
InputStream resourceAsStream = Test01.class.getClassLoader().getResourceAsStream("jdbc.properties");
properties.load(resourceAsStream);
String driver = properties.getProperty("driver");
String url = properties.getProperty("url");
String name = properties.getProperty("name");
String pwd = properties.getProperty("pwd");
// 获取连接
Class.forName(driver);
Connection connection = DriverManager.getConnection(url, name, pwd);
System.out.println(connection);
Spring IoC的实现步骤
- 定义需要解耦的类。
- 将类注入到Spring容器:
<bean id="唯一标识" class="类"></bean>
- 从Spring容器中获取对象:
- 加载配置文件:
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
- 获取对象:
Student stu = (Student) context.getBean("stu");
- 加载配置文件:
IoC容器的类型
BeanFactory
(父)ApplicationContext
(子)
常见的容器实现有:
ClassPathXmlApplicationContext
:通过加载主配置文件的相对路径获取Spring容器。FileSystemXmlApplicationContext
:通过加载主配置文件的绝对路径获取Spring容器。AnnotationConfigApplicationContext
:加载配置类获取Spring容器。
什么是依赖注入(DI)
依赖注入(Dependency Injection,DI)是实现控制反转的关键技术,主要通过将Bean之间的依赖关系通过构造方法或setter方法注入,降低了组件之间的耦合度。
DI的实现方式
- set注入:
- 将需要注入的对象属性生成set方法
- 在xml中配置
<property name="bean的id" ref="引用其他bean对象id" value="值" />
<!--========set注入对象===========-->
<bean id="controller" class="com.yk.controller.UserControllerImpl">
<property name="service" ref="service"/>
</bean>
<bean id="service" class="com.yk.service.UserServiceImpl">
<property name="dao" ref="dao"/>
</bean>
<bean id="dao" class="com.yk.dao.UserDaoImpl"/>
<!--========set注入基本类型==========-->
<bean id="student" class="com.yk.pojo.Student">
<property name="name" value="大白"/>
<property name="age" value="12"/>
</bean>
<!--==========set注入复杂类型=========-->
<bean id="teacher" class="com.yk.pojo.Teacher">
<property name="myList">
<list>
<value>晓晓</value>
<value>小陆</value>
<value>王强</value>
<value>马猴</value>
</list>
</property>
<property name="myArray">
<array>
<value>高伟</value>
<value>刘思琪</value>
<value>小黑</value>
<value>朱红</value>
</array>
</property>
<property name="mySet">
<set>
<value>苏州</value>
<value>杭州</value>
<value>南京</value>
<value>西塘</value>
</set>
</property>
<property name="myMap">
<map>
<entry key="访客" value="迷彩"/>
<entry key="钥匙" value="房子"/>
</map>
</property>
<property name="myPop">
<props >
<prop key="日本">樱花</prop>
<prop key="中国">牡丹</prop>
</props>
</property>
</bean>
-
构造注入:
-
将需要注入的对象属性生成有参无参构造方法
-
进行配置
<constructor-arg name="对象名" type="对象类型" index="下标" value="值" ref="引用其他bean对象">
-
-
构造注入不支持注入复杂类型
<!--基本类型-->
<bean id="student1" class="com.yk.pojo.Student">
<constructor-arg name="name" value="小天"/>
<constructor-arg name="age" value="21"/>
</bean>
<bean id="student2" class="com.yk.pojo.Student">
<constructor-arg type="java.lang.String" value="叶故"/>
<constructor-arg type="java.lang.String" value="22"/>
</bean>
<bean id="student3" class="com.yk.pojo.Student">
<constructor-arg index="0" value="明月"/>
<constructor-arg index="1" value="18"/>
</bean>
<!--构造注入对象-->
<bean id="controller1" class="com.yk.controller.UserControllerImpl">
<constructor-arg name="service" ref="service1"/>
</bean>
<bean id="service1" class="com.yk.service.UserServiceImpl">
<constructor-arg name="dao" ref="dao1"/>
</bean>
<bean id="dao1" class="com.yk.dao.UserDaoImpl"/>
IOC容器对bean的管理
Spring IoC容器对Bean的管理
1. Bean的实例化
1.1 通过构造方法(默认方式)
在Spring中,最常见的Bean实例化方式是通过构造方法。例如,下面的配置创建了一个Student
对象:
<!-- 使用构造方法进行Bean实例化 -->
<bean id="student" class="pojo.Student"></bean>
1.2 通过工厂方法
你可以创建一个工厂类来返回Student
对象。以下是实现示例:
public class BeansFactory {
public Student createStu() {
System.out.println("执行普通工厂方法");
return new Student();
}
}
在XML文件中,使用以下配置:
<!-- 使用工厂方法进行Bean实例化 -->
<bean id="student" class="pojo.Student" factory-bean="factory" factory-method="createStu"/>
<bean id="factory" class="factory.BeansFactory"/>
1.3 静态工厂方法
Spring也支持静态工厂方法来实例化Bean,示例如下:
<!-- 使用静态工厂方法进行Bean实例化 -->
<bean id="student" class="factory.StaticBeansFactory" factory-method="createStudent"/>
2. Bean的作用域
Bean的作用域决定了Spring如何创建和管理JavaBean实例,以下是常用的作用域配置:
<!-- Bean的作用域配置 -->
<!-- 单例(默认) -->
<bean id="teacher1" class="pojo.Teacher" scope="singleton"/>
<!-- 多例 -->
<bean id="teacher2" class="pojo.Teacher" scope="prototype"/>
可用的作用域属性:
singleton
:单例(默认)。prototype
:多例。request
:每个请求创建一个。session
:每个会话创建一个。
3. Bean的生命周期
Bean的生命周期管理涉及多个重要步骤,以下是主要阶段:
- 实例化
- 属性赋值(DI)
- 初始化
- 通过接口
InitializingBean
- 通过属性
init-method=""
- 通过接口
- 使用
- 销毁
- 通过接口
DisposableBean
- 通过属性
destroy-method=""
- 通过接口
示例实体类:
package pojo;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
public class User implements InitializingBean, DisposableBean {
private String uname;
public User() {
System.out.println("1. 实例化");
}
public void setUname(String uname) {
System.out.println("2. 属性赋值");
this.uname = uname;
}
@Override
public void afterPropertiesSet() {
System.out.println("3. Bean对象的初始化 --> 接口");
}
public void doInit() {
System.out.println("3. Bean对象的初始化 --> 属性");
}
@Override
public void destroy() {
System.out.println("5. Bean对象的销毁 --> 接口");
}
public void dest() {
System.out.println("5. Bean对象的销毁 --> 属性");
}
}
XML配置:
<!-- Bean的生命周期管理 -->
<bean id="user" class="pojo.User" init-method="doInit" destroy-method="dest">
<property name="uname" value="大宝"/>
</bean>