4. DI相关内容
我们先来思考
-
向一个类中传递数据的方式有几种?
- 普通方法(set 方法)
- 构造方法
-
依赖注入描述了在容器中建立 bean 与 bean 之间的依赖关系的过程,如果 bean 运行需要的是数字或字符串呢?
- 引用类型
- 简单类型(基本数据类型与 String)
Spring 就是基于上面这些知识点,为我们提供了两种注入方式,分别是:
-
setter 注入
- 简单类型
- 引用类型
-
构造器注入
- 简单类型
- 引用类型
1. setter 注入
- 对于 setter 方式注入引用类型的方式之前已经学习过,快速回顾下:
- 在 bean 中定义引用类型属性,并提供可访问的set方法
public class BookServiceImpl implements BookService {
private BookDao bookDao;
public void setBookDao(BookDao bookDao) {
this.bookDao = bookDao;
}
}
- 配置中使用?property 标签 ref?属性注入引用类型对象
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
<property name="bookDao" ref="bookDao"/>
</bean>
<bean id="bookDao" class="com.itheima.dao.imipl.BookDaoImpl"/>
?
1.1 注入引用数据类型
?
<?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/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl"/>
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl"/>
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
<property name="bookDao" ref="bookDao"/>
<property name="userDao" ref="userDao"/>
</bean>
</beans>
?
1.2 注入简单数据类型
?
思考:
引用类型使用的是?<property name="" ref=""/>
?,简单数据类型还是使用 ref 么?
ref 是指向 Spring 的 IOC 容器中的另一个 bean 对象的,对于简单数据类型,没有对应的 bean 对象,该如何配置?
?
<?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/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
<property name="databaseName" value="mysql"/>
<property name="connectionNum" value="10"/>
</bean>
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl"/>
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
<property name="bookDao" ref="bookDao"/>
<property name="userDao" ref="userDao"/>
</bean>
</beans>
?
注意:两个 property 注入标签的顺序可以任意。
对于 setter 注入方式的基本使用就已经介绍完了,
- 对于引用数据类型使用的是?
<property name="" ref=""/>
? - 对于简单数据类型使用的是?
<property name="" value=""/>
?
2. 构造器注入
?
2.1 构造器注入引用数据类型
?
<?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/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl"/>
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
<constructor-arg name="bookDao" ref="bookDao"/>
</bean>
</beans>
?
说明:
标签
- name 属性对应的值为构造函数中方法形参的参数名,必须要保持一致。
- ref 属性指向的是 spring 的 IOC 容器中其他 bean 对象。
2.2 构造器注入多个引用数据类型
?
<?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/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl"/>
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl"/>
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
<constructor-arg name="bookDao" ref="bookDao"/>
<constructor-arg name="userDao" ref="userDao"/>
</bean>
</beans>
?
说明:这两个<contructor-arg>
? 的配置顺序可以任意
?
2.3 构造器注入多个简单数据类型
?
<?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/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
<constructor-arg name="databaseName" value="mysql"/>
<constructor-arg name="connectionNum" value="666"/>
</bean>
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl"/>
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
<constructor-arg name="bookDao" ref="bookDao"/>
<constructor-arg name="userDao" ref="userDao"/>
</bean>
</beans>
?
说明:这两个<contructor-arg>
? 的配置顺序可以任意
?
上面已经完成了构造函数注入的基本使用,但是会存在一些问题:
??
?
- 当构造函数中方法的参数名发生变化后,配置文件中的 name 属性也需要跟着变
- 这两块存在紧耦合,具体该如何解决?
在解决这个问题之前,需要提前说明的是,这个参数名发生变化的情况并不多,所以上面的还是比较主流的配置方式,下面介绍的,大家都以了解为主。
方式一:删除 name 属性,添加 type 属性,按照类型注入
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
<constructor-arg type="int" value="10"/>
<constructor-arg type="java.lang.String" value="mysql"/>
</bean>
- 这种方式可以解决构造函数形参名发生变化带来的耦合问题
- 但是如果构造方法参数中有类型相同的参数,这种方式就不太好实现了
?
方式二:删除 type 属性,添加 index 属性,按照索引下标注入,下标从 0 开始
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
<constructor-arg index="1" value="100"/>
<constructor-arg index="0" value="mysql"/>
</bean>
- 这种方式可以解决参数类型重复问题
- 但是如果构造方法参数顺序发生变化后,这种方式又带来了耦合问题
?
介绍完两种参数的注入方式,具体我们该如何选择呢?
-
强制依赖使用构造器进行,使用 setter 注入有概率不进行注入导致 null 对象出现
- 强制依赖指对象在创建的过程中必须要注入指定的参数
-
可选依赖使用 setter 注入进行,灵活性强
- 可选依赖指对象在创建过程中注入的参数可有可无
-
Spring 框架倡导使用构造器,第三方框架内部大多数采用构造器注入的形式进行数据初始化,相对严谨
-
如果有必要可以两者同时使用,使用构造器注入完成强制依赖的注入,使用 setter 注入完成可选依赖的注入
-
实际开发过程中还要根据实际情况分析,如果受控对象没有提供 setter 方法就必须使用构造器注入
-
自己开发的模块推荐使用 setter 注入
3. 关于 setter 注入和构造器注入的小结
?
-
setter 注入
-
简单数据类型
<bean ...> <property name="" value=""/> </bean>
-
引用数据类型
<bean ...> <property name="" ref=""/> </bean>
-
-
构造器注入
-
简单数据类型
<bean ...> <constructor-arg name="" index="" type="" value=""/> </bean>
-
引用数据类型
<bean ...> <constructor-arg name="" index="" type="" ref=""/> </bean>
-
-
依赖注入的方式选择上
- 建议使用 setter 注入
- 第三方技术根据情况选择
4. 自动装配
?
前面花了大量的时间把 Spring 的注入去学习了下,总结起来就一个字麻烦。
问:麻烦在哪?
答:配置文件的编写配置上。
问:有更简单方式么?
答:有,自动配置
?
4.1 什么是依赖自动装配?
?
IoC 容器根据 bean 所依赖的资源在容器中自动查找并注入到 bean 中的过程称为自动装配
?
4.2 自动装配方式有哪些?
- 按类型(常用)
- 按名称
- 按构造方法
- 不启用自动装配
?
4.3 完成自动装配的配置
?
自动装配只需要修改 applicationContext.xml 配置文件即可:
(1)将?<property>
? 标签删除
(2)在?<bean>
? 标签中添加 autowire 属性
首先来实现按照类型注入的配置
<?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/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean class="com.itheima.dao.impl.BookDaoImpl"/>
<!--autowire属性:开启自动装配,通常使用按类型装配-->
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl" autowire="byType"/>
</beans>
?
注意事项:
- 需要注入属性的类中对应属性的 setter 方法不能省略
- 被注入的对象必须要被 Spring 的 IOC 容器管理
- 按照类型在 Spring 的 IOC 容器中如果找到多个对象,会报?
NoUniqueBeanDefinitionException
?
一个类型在 IOC 中有多个对象,还想要注入成功,这个时候就需要按照名称注入,配置方式为:
<?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/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean class="com.itheima.dao.impl.BookDaoImpl"/>
<!--autowire属性:开启自动装配,通常使用按类型装配-->
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl" autowire="byName"/>
</beans>
?
注意事项:
-
按照名称注入中的名称指的是什么?
????
-
bookDao 是 private 修饰的,外部类无法直接方法
-
外部类只能通过属性的 set 方法进行访问
-
对外部类来说,setBookDao 方法名,去掉 set 后首字母小写是其属性名
- 为什么是去掉 set 首字母小写?
- 这个规则是 set 方法生成的默认规则,set 方法的生成是把属性名首字母大写前面加 set 形成的方法名
-
所以按照名称注入,其实是和对应的 set 方法有关,但是如果按照标准起名称,属性名和 set 对应的名是一致的
-
-
如果按照名称去找对应的 bean 对象,找不到则注入 Null
-
当某一个类型在 IOC 容器中有多个对象,按照名称注入只找其指定名称对应的 bean 对象,不会报错
两种方式介绍完后,以后用的更多的是?按照类型?注入。
?
最后对于依赖注入,需要注意一些其他的配置特征:
- 自动装配用于引用类型依赖注入,不能对简单类型进行操作
- 使用按类型装配时(byType)必须保障容器中相同类型的 bean 唯一,推荐使用
- 使用按名称装配时(byName)必须保障容器中具有指定名称的 bean,因变量名与配置耦合,不推荐使用
- 自动装配优先级低于 setter 注入与构造器注入,同时出现时自动装配配置失效
?
5. 集合注入
?
常见的集合类型
- 数组
- List
- Set
- Map
- Properties
5.1 注入数组类型数据
<property name="array">
<array>
<value>100</value>
<value>200</value>
<value>300</value>
</array>
</property>
5.2 注入 List 类型数据
<property name="list">
<list>
<value>itcast</value>
<value>itheima</value>
<value>boxuegu</value>
<value>chuanzhihui</value>
</list>
</property>
5.3 注入 Set 类型数据
<property name="set">
<set>
<value>itcast</value>
<value>itheima</value>
<value>boxuegu</value>
<value>boxuegu</value>
</set>
</property>
5.4 注入 Map 类型数据
<property name="map">
<map>
<entry key="country" value="china"/>
<entry key="province" value="henan"/>
<entry key="city" value="kaifeng"/>
</map>
</property>
5.5 注入 Properties 类型数据
<property name="properties">
<props>
<prop key="country">china</prop>
<prop key="province">henan</prop>
<prop key="city">kaifeng</prop>
</props>
</property>
?
说明:
- property 标签表示 setter 方式注入,构造方式注入 constructor-arg 标签内部也可以写?
<array>
?、<list>
?、<set>
?、<map>
?、<props>
? 标签 - List 的底层也是通过数组实现的,所以?
<list>
? 和<array>
? 标签是可以混用 - 集合中要添加引用类型,只需要把?
<value>
? 标签改成<ref>
? 标签,这种方式用的比较少