我们知道,Spring 的核心功能就是控制反转和依赖注入。前面介绍了控制反转,那依赖注入是什么呢?
上面是我们之前对于依赖注入的一个通俗解释。这里再着重强调一下 IOC 控制反转与 DI 依赖注入的关系:
IOC 控制反转是将对象实例化的动作交由了 Spring 框架, 它的作用是降低了程序的耦合,不需要我们手动的创建对象,但是程序的耦合性还是存在。
对象中肯定会有一些其余对象的引用,那么这种引用就称为对象的依赖,而 DI 依赖注入其实是 IOC 设计思想的一种表现形式。
对于这种属性依赖,我们无需手动赋值,也是将赋值的动作交给 Spring ,这种操作就是依赖注入。
依赖注入方式:
下面的案例将通过 xml 方式实现依赖注入。
步骤:
创建工程、导入依赖:参考之前创建过的IOC工程。
创建 Servcie 接口和接口实现类:
//接口代码
public interface UserService {
public void deleteById(Integer id);
}
//实现类代码
public class UserServiceImpl implements UserService {
private UserDao userDao;
public UserDao getUserDao() {
return userDao;
}
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void deleteById(Integer id) {
System.out.println("删除的方法执行");
}
}
创建UserDao 接口和实现类:
//dao接口代码
public interface UserDao {
}
//dao实现类代码
public class UserDaoImpl implements UserDao {
}
代码解析: dao的接口和实现类中并没有方法,只是为了测试作为service中的属性依赖,可以实现由 Spring 完成动态注入。
Spring 的核心配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<bean id="userDao" class="com.ty.dao.UserDaoImpl"></bean>
<bean id="userService" class="com.ty.service.UserServiceImpl">
<property name="userDao" ref="userDao"></property>
<property name="userName" value="ty"></property>
</bean>
</beans>
配置解析:
bean
标签描述一个被实例化的类,property
则表示一个类中的属性property
标签中的name
属性一般写成类中的属性名称, 实际上,起决定作用的并不是属性名,而是set
方法的名称。ref
表示当前的属性是一个引用对象,而引用的是谁呢? ref
中的值,必须是在容器中已经实例化的一个引用对象的唯一标识。value
表示当前的属性可以直接赋值,在 value
标签中 填写要赋予的值即可。运行结果
结果解析
成功打印了 service
的类属性 Userdao
实例和字符串属性 userName
的值,说明注入成功。
注意事项:
如果通过 property
属性标签来实现属性注入,那么类中必须定义该属性的set
方法。
Integer
、Double
、Boolean
;String
;map
、set
、list
。对于基本类型和字符串类型,在 xml 的配置文件中,通过 value 属性即可以复制,我们上个案例已经测试过,这里不做赘述,主要演示集合类型的属性注入测试。
搭建工程,引入依赖,配置文件:参考前面的代码。
a.编写一个java类,定义多种类型的集合属性
代码如下:
public class User {
private Integer id;
private String name;
private Object [] array;
private List list;
private Map map;
//省略get和set方法
}
上面的类中,我们定义了多个属性, array
数组、list
和 map
集合。
b.数组的属性注入
配置文件如下:
<!-- 数组的属性注入 -->
<bean id="user" class="com.ty.entity.User">
<property name="array">
<array>
<value>tom</value>
<value>jerry</value>
</array>
</property>
</bean>
配置解析:
property
中的 name
是Java 类中数组的属性名称,用于 set
方法提供注入;array
标签是固定的,不能变化,表示属性是一个数组,所以加在了 property
的属性内部;value
表示数组中的值,因为数组可以存储多个值,所以每一个数组的值,通过一个 value
标签声明。运行结果:
成功打印了 user
类中的 array
数组中在 xml 文件中配置的数组值,定义好的 tom 和 jerry。数组的依赖注入完成。
c.集合的属性注入
改造 xml 的配置文件,实现 list
集合的属性配置。
<!-- 集合的属性注入 -->
<bean id="user" class="com.ty.entity.User">
<property name="list">
<list>
<value>hello</value>
<value>world</value>
</list>
</property>
</bean>
配置解析:
property
中的 name
是 java 类中数组的属性名称 用于 set
方法提供注入;list
标签是固定的,不能变化,表示属性是一个 list
集合 ,所以加在了 property
的属性内部;value
表示集合中的值,因为集合可以存储多个值,所以每一个集合中的值,通过一个 value
标签声明。运行结果:
list
集合中的两个数据都打印出来了,说明 list
集合的 xml 依赖注入完成。
d. map集合的注入实现
继续更改 xml 文件的配置 ,如下:
<bean id="user" class="com.ty.entity.User">
<property name="map">
<map>
<entry key="name" value="ty"></entry>
<entry key="age" value="25"></entry>
</map>
</property>
</bean>
配置解析:
property
中的 name
是Java 类中map
的属性名称,用于 set
方法提供注入;map
标签是固定的,不能变化,表示属性是一个 map
集合 ,所以加在了 property
的属性内部;entry
标签固定表示 map
中的一对键值对, key
表示键值对的键,value
表示键值对的值。在正式使用注解之前,我们首先介绍下注解语法以及作用。
@Autowired: 此注解自动按照类型注入。从容器中寻找符合依赖类型的实例,当使用该注解注入属性时,set
方法可以省略。但是因为按照类型匹配,如果容器中有多个匹配的类型,会抛出异常,需要指定引入的实例 id。如果找不到匹配的实例,那么也会抛出异常;
@Qualifier: 此注解不能单独使用,它的作用是在@Autowired
按照类型注入的基础上,再按照 Bean 的 id 注入。所以如果是使用了 @Autowire
注解自动注入,但是容器中却有多个匹配的实例,可以搭配此注解,指定需要注入的实例 id;
@Resource :此注解的作用是指定依赖按照 id 注入,还是按照类型注入。当只使用注解,但是不指定注入方式的时候,默认按照 id 注入,找不到再按照类型注入。
①为了测试效果,我们创建 Service 和 Dao两个类, Dao 作为 Service 的依赖。代码如下:
//service实现类的代码
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
public void saveUser() {
System.out.println("UserService保存方法已执行");
}
}
//dao实现类的代码
@Repository
public class UserDaoImpl implements UserDao {
public void saveUser() {
System.out.println("UserDao保存方法已执行");
}
}
代码解析:
上面代码中两个类的实例化方式都是通过注解注入到容器, 并且在 service 实现类中的 userDao 属性上面加了注解 @Autowired
。
我们首先测试下:能否通过这个注解,实现依赖注入,另外再测试下它是否是按照类型注入。
②配置文件的内容为注解实现 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"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.ty"></context:component-scan>
</beans>
配置文件解析: 通过组件扫描来实例化容器。
③编写测试代码
public class SpringAnTest {
public static void main(String[] args) {
ApplicationContext context =
new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
UserService userService = context.getBean(UserService.class);
userService.saveUser();
}
}
运行结果:
结果解析:
可以看到 service 中的保存方法执行,并且通过 dao 的实例调用的方法也执行了,说明 @Autowired
注解实现了属性 userDao
的注入。
当然这种操作是小儿科,没有一个同学觉得他有什么。 我们验证下它的特点:set 方法我们是省略了,那么它是否按照类型注入的呢?
如果我们的实现类中有多个 userDao
接口的实现类呢,又该如何呢?
④添加 UserDaoImpl2 一样实现 userDao 的接口,代码如下:
@Repository
public class UserDaoImpl2 implements UserDao {
public void saveUser() {
System.out.println("UserDao2的保存方法已执行");
}
}
运行结果:
结果解析:
打印的异常堆栈信息显示错误原因为没有指定的 bean 实例 (UserDao
类型的),期待单个 bean 匹配,但是找到了两个。一个是 userDaoImpl
一个是 userDaoImpl2
。
这可以证明: @Autowired
注解是按照类型注入,如果该类型有多个实例就会报错。
那么,针对这种多个接口实例的情况,怎么解决的呢?继续下面的注解学习。
它的作用是在按照类型注入的基础之上,再按照 Bean 的 id 注入,不能单独使用,需要搭配上面的 @Autiwired
注解。
①在两个实现类的基础之上改造代码如下:
@Service
public class UserServiceImpl implements UserService {
@Qualifier("userDaoImpl")
@Autowired
private UserDao userDao;
public void saveUser() {
System.out.println("UserService的保存方法已执行");
userDao.saveUser();
}
}
代码解析:
在属性注入的地方,通过注解 @Qualifier
的参数,指定了注入的 bean 实例 id 为 userDaoImpl
。
执行结果:
结果解析:
此时的方法可以正常执行,而且执行的就是 userDaoImpl
中的方法。
②继续改造 service 的代码,将 @Qualifier 注解中的值换成 userDaoImpl2,结果如下:
通过修改 @Qualifier
注解中 id 的属性值 ,可以分别注入不同的实现类,证明了 @Qualifier
注解的作用。
此注解的作用是指定依赖按照 id 注入还是按照类型注入。当只使用注解,但是不指定注入方式的时候,默认按照 id 注入,找不到时再按照类型注入。
语法如下:
@Resource //默认按照 id 为 userDao的bean实例注入
@Resource(name="userDao") //按照 id 为 userDao的bean实例注入
@Resource(type="UserDao") //按照 类型 为 UserDao的bean实例注入
这里就只做个语法的介绍,注解的使用大同小异,按照上方步骤自行测试即可。
@Autowired
@Qualifier
@Resources
;ps:以上内容来自对慕课教程的学习与总结