依赖注入
依赖注入方式
我们向一个类中传递数据有两种方式:
普通方法(setter方法)
构造方法
依赖注入描述了在容器中建立bean与bean之间依赖关系的过程,如果bean运行需要的是数字或字符串呢?
因此bean依赖注入分两种情况:
- 注入引用类型
- 注入简单类型(基本数据类型与String)
所以依赖注入的方式分为下面四种:
setter注入
- setter方法注入简单类型
- setter方法注入引用类型 (前面我们在DI快速入门中, 使用的就是setter方法注入引用类型)
构造器注入
- 构造方法注入简单类型
- 构造方法注入引用类型
Setter注入-引用类型
Setter注入引用类型在DI快速入门中讲解过, 这里不再演示
使用步骤:
在bean中定义引用类型属性并提供可访问的set方法
配置中使用property标签ref属性注入引用类型对象
Setter注入-简单类型
使用步骤:
在bean中定义简单类型属性并提供可访问的set方法
public class BookDaoImpl implements BookDao {
// 定义简单类型属性
private int connectionNum;
private String databaseName;
public void save() {
System.out.println("book dao save: " + connectionNum + " " + databaseName);
}
// 提供setter方法
public void setConnectionNum(int connectionNum) {
this.connectionNum = connectionNum;
}
public void setDatabaseName(String databaseName) {
this.databaseName = databaseName;
}
}
配置中使用property标签value属性注入简单类型数据;
- 注意: 使用value属性, Spring内部会自动进行类型转换
<bean id="bookDao" class="com.chenyq.dao.impl.BookDaoImpl">
<property name="connectionNum" value="10"/>
<property name="databaseName" value="mysql"/>
</bean>
构造器注入-引用类型
使用步骤:
在bean中定义引用类型属性, 并提供可访问的构造方法
public class BookServiceImpl implements BookService {
// 定义引用类型属性
private BookDao bookDao;
// 定义构造器
public BookServiceImpl(BookDao bookDao) {
this.bookDao = bookDao;
}
public void save() {
System.out.println("book service save");
bookDao.save();
}
}
配置中使用constructor-arg标签ref属性注入引用类型对象
- constructor-arg标签中name属性表示要注入的构造器的形参名
- constructor-arg标签中ref属性表示要注入的对象
<bean id="bookDao" class="com.chenyq.dao.impl.BookDaoImpl"/>
<bean id="bookService" class="com.chenyq.service.impl.BookServiceImpl">
<constructor-arg name="bookDao" ref="bookDao"/>
</bean>
构造器注入-简单类型
实现步骤:
在bean中定义引用类型属性, 并提供可访问的构造方法
public class BookDaoImpl implements BookDao {
private String databaseName;
private int connectionNum;
// 提供构造方法
public BookDaoImpl(String databaseName, int connectionNum) {
this.databaseName = databaseName;
this.connectionNum = connectionNum;
}
public void save() {
System.out.println("book dao save..." + databaseName + " " + connectionNum);
}
}
配置中使用constructor-arg标签value属性注入简单类型数据
<bean id="bookDao" class="com.chenyq.dao.impl.BookDaoImpl">
<constructor-arg name="connectionNum" value="10"/>
<constructor-arg name="databaseName" value="mysql"/>
</bean>
构造器注入-参数适配
在上面构造器注入的配置中, 一但构造器的参数名改变, 那么我们配置文件中的name属性也必须改变
我们可以使用参数适配解决这个耦合问题
配置中使用constructor-arg标签type属性设置按形参类型注入
<bean id="bookDao" class="com.chenyq.dao.impl.BookDaoImpl">
<constructor-arg type="int" value="10"/>
<constructor-arg type="java.lang.String" value="mysql"/>
</bean>
配置中使用constructor-arg标签index属性设置按形参位置注入
<bean id="bookDao" class="com.chenyq.dao.impl.BookDaoImpl">
<constructor-arg index="1" value="10"/>
<constructor-arg index="0" value="mysql"/>
</bean>
依赖注入方式的选择
-
强制依赖使用构造器进行,使用setter注入有概率不进行注入导致null对象出现
-
可选依赖使用setter注入进行,灵活性强
-
Spring框架倡导使用构造器,第三方框架内部大多数采用构造器注入的形式进行数据初始化,相对严谨
-
如果有必要可以两者同时使用,使用构造器注入完成强制依赖的注入,使用setter注入完成可选依赖的注入
-
实际开发过程中还要根据实际情况分析,如果受控对象没有提供setter方法就必须使用构造器注入
-
自己开发的模块
推荐使用setter注入
依赖自动装配
依赖自动装配:
IoC容器根据bean所依赖的资源在容器中自动查找并注入到bean中的过程称为自动装配
自动装配的方式:
按类型(常用)
按名称
按构造方法
不启用自动装配
使用步骤:
因为要自动注入, 所以setter方法或者构造器我们还是需要提供
public class BookServiceImpl implements BookService {
// 定义引用类型属性
private BookDao bookDao;
public void save() {
System.out.println("book service save");
bookDao.save();
}
// 提供setter方法
public void setBookDao(BookDao bookDao) {
this.bookDao = bookDao;
}
}
配置中使用bean标签autowire属性设置自动装配的类型, autowire有下面几个属性
- byType: 按类型装配
- byName: 按名称装配
- constructor: 按构造器装配
- default: 默认
- no: 不开起
<bean id="bookDao" class="com.chenyq.dao.impl.BookDaoImpl" />
<bean id="bookService" class="com.chenyq.service.impl.BookServiceImpl" autowire="byType"/>
自动装配用于引用类型依赖注入,不能对简单类型进行操作
使用按类型装配时(byType)必须保障容器中相同类型的bean唯一,推荐使用
使用按名称装配时(byName)必须保障容器中具有指定名称的bean,因变量名与配置耦合,不推荐使用
自动装配优先级低于setter注入与构造器注入,同时出现时自动装配配置失效
集合注入
前面我们注入的都是单个数据, 这里我们讲解注入多个数据
提供setter方法
public class BookDaoImpl implements BookDao {
private int[] array;
private List<String> list;
private Set<String> set;
private Map<String, String> map;
private Properties properties;
public void save() {
System.out.println(Arrays.toString(array));
System.out.println(list);
System.out.println(set);
System.out.println(map);
System.out.println(properties);
}
public void setArray(int[] array) {
this.array = array;
}
public void setList(List<String> list) {
this.list = list;
}
public void setSet(Set<String> set) {
this.set = set;
}
public void setMap(Map<String, String> map) {
this.map = map;
}
public void setProperties(Properties properties) {
this.properties = properties;
}
}
注入数组对象
<bean id="bookDao" class="com.chenyq.dao.impl.BookDaoImpl">
<property name="array">
<array>
<value>10</value>
<value>20</value>
<value>30</value>
</array>
</property>
</bean>
注入List对象
<bean id="bookDao" class="com.chenyq.dao.impl.BookDaoImpl">
<property name="list">
<list>
<value>chenyq</value>
<value>coder</value>
<value>why</value>
</list>
</property>
</bean>
注入Set对象
<bean id="bookDao" class="com.chenyq.dao.impl.BookDaoImpl">
<property name="set">
<set>
<value>100</value>
<value>100</value>
<value>200</value>
<value>200</value>
</set>
</property>
</bean>
注入Map对象
<bean id="bookDao" class="com.chenyq.dao.impl.BookDaoImpl">
<property name="map">
<map>
<entry key="name" value="chenyq"/>
<entry key="city" value="成都"/>
</map>
</property>
</bean>
注入properties对象
<bean id="bookDao" class="com.chenyq.dao.impl.BookDaoImpl">
<property name="properties">
<props>
<prop key="country">中国</prop>
<prop key="city">成都</prop>
</props>
</property>
</bean>
执行查看打印结果
public class App {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
BookDao bookDao = (BookDao) ctx.getBean("bookDao");
bookDao.save();
}
}
打印结果:
[10, 20, 30]
[chenyq, coder, why]
[100, 200]
{name=chenyq, city=成都}
{city=成都, country=中国}