Spring核心功能之依赖注入(DI)

1 Spring DI(依赖注入)之XML配置

我们知道,Spring 的核心功能就是控制反转和依赖注入。前面介绍了控制反转,那依赖注入是什么呢?

1.1概念介绍

Spring核心功能之依赖注入(DI)_第1张图片
上面是我们之前对于依赖注入的一个通俗解释。这里再着重强调一下 IOC 控制反转与 DI 依赖注入的关系:

IOC 控制反转是将对象实例化的动作交由了 Spring 框架, 它的作用是降低了程序的耦合,不需要我们手动的创建对象,但是程序的耦合性还是存在。

对象中肯定会有一些其余对象的引用,那么这种引用就称为对象的依赖,而 DI 依赖注入其实是 IOC 设计思想的一种表现形式。

对于这种属性依赖,我们无需手动赋值,也是将赋值的动作交给 Spring ,这种操作就是依赖注入。

依赖注入方式:

  • 第一种方式是通过 xml 配置的方式实现;
  • 第二种方式是在属性或者方法上使用注解的方式实现。

下面的案例将通过 xml 方式实现依赖注入。

1.2 案例实现:

步骤:

  • 创建一个 maven 工程
  • 导入Spring 使用的依赖
  • 编写业务层的 Service 和持久层的 Dao java 类
  • 编写 Spring 的配置文件

创建工程、导入依赖:参考之前创建过的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标签中 填写要赋予的值即可。

运行结果
Spring核心功能之依赖注入(DI)_第2张图片
结果解析
成功打印了 service 的类属性 Userdao 实例和字符串属性 userName的值,说明注入成功。

注意事项:
如果通过 property 属性标签来实现属性注入,那么类中必须定义该属性的set方法。

1.3 总结:

  • 依赖注入实际上是 IOC 设计思想的一种具体实现;
  • 依赖注入可以通过 xml 配置实现 ,可以通过注解实现;
  • xml的依赖注入是依托于类中的 set 方法实现的。

2 Spring DI(依赖注入)之多种类型属性

2.1 属性类型分类

  • 基本类型包装类,比如 IntegerDoubleBoolean
  • 字符串类型,比如 String
  • 类类型,比如自定义的 java 类;
  • 集合类型,比如 mapsetlist

对于基本类型和字符串类型,在 xml 的配置文件中,通过 value 属性即可以复制,我们上个案例已经测试过,这里不做赘述,主要演示集合类型的属性注入测试。

2.2 案例实现:

搭建工程,引入依赖,配置文件:参考前面的代码。

a.编写一个java类,定义多种类型的集合属性
代码如下:

public class User {
private Integer id;
private String name;
private Object [] array;
private List    list;
private Map  map;
//省略get和set方法

}

上面的类中,我们定义了多个属性, array 数组、listmap 集合。

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 标签声明。

运行结果:
Spring核心功能之依赖注入(DI)_第3张图片
成功打印了 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 标签声明。

运行结果:
Spring核心功能之依赖注入(DI)_第4张图片
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表示键值对的值。

运行结果:
Spring核心功能之依赖注入(DI)_第5张图片
map 集合属性注入成功。

3 Spring DI(依赖注入)之注解配置

3.1 注解的介绍

在正式使用注解之前,我们首先介绍下注解语法以及作用。

@Autowired: 此注解自动按照类型注入。从容器中寻找符合依赖类型的实例,当使用该注解注入属性时,set 方法可以省略。但是因为按照类型匹配,如果容器中有多个匹配的类型,会抛出异常,需要指定引入的实例 id。如果找不到匹配的实例,那么也会抛出异常;

@Qualifier: 此注解不能单独使用,它的作用是在@Autowired按照类型注入的基础上,再按照 Bean 的 id 注入。所以如果是使用了 @Autowire 注解自动注入,但是容器中却有多个匹配的实例,可以搭配此注解,指定需要注入的实例 id;

@Resource :此注解的作用是指定依赖按照 id 注入,还是按照类型注入。当只使用注解,但是不指定注入方式的时候,默认按照 id 注入,找不到再按照类型注入。

3.2 @Autowired 注解

①为了测试效果,我们创建 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();
    }
}

运行结果:
Spring核心功能之依赖注入(DI)_第6张图片
结果解析:
可以看到 service 中的保存方法执行,并且通过 dao 的实例调用的方法也执行了,说明 @Autowired 注解实现了属性 userDao 的注入。

当然这种操作是小儿科,没有一个同学觉得他有什么。 我们验证下它的特点:set 方法我们是省略了,那么它是否按照类型注入的呢?

如果我们的实现类中有多个 userDao 接口的实现类呢,又该如何呢?

④添加 UserDaoImpl2 一样实现 userDao 的接口,代码如下:

@Repository
public class UserDaoImpl2 implements  UserDao {
    public void saveUser() {
        System.out.println("UserDao2的保存方法已执行");
    }
}

运行结果:
Spring核心功能之依赖注入(DI)_第7张图片
结果解析:
打印的异常堆栈信息显示错误原因为没有指定的 bean 实例 (UserDao 类型的),期待单个 bean 匹配,但是找到了两个。一个是 userDaoImpl 一个是 userDaoImpl2

这可以证明: @Autowired 注解是按照类型注入,如果该类型有多个实例就会报错。

那么,针对这种多个接口实例的情况,怎么解决的呢?继续下面的注解学习。

3.3 @Qualifier 注解

它的作用是在按照类型注入的基础之上,再按照 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

执行结果:
Spring核心功能之依赖注入(DI)_第8张图片
结果解析:
此时的方法可以正常执行,而且执行的就是 userDaoImpl 中的方法。

②继续改造 service 的代码,将 @Qualifier 注解中的值换成 userDaoImpl2,结果如下:
Spring核心功能之依赖注入(DI)_第9张图片
通过修改 @Qualifier 注解中 id 的属性值 ,可以分别注入不同的实现类,证明了 @Qualifier 注解的作用。

3.4 @Resources 注解

此注解的作用是指定依赖按照 id 注入还是按照类型注入。当只使用注解,但是不指定注入方式的时候,默认按照 id 注入,找不到时再按照类型注入。

语法如下:

@Resource   //默认按照 id 为 userDao的bean实例注入    
@Resource(name="userDao")  //按照 id 为 userDao的bean实例注入    
@Resource(type="UserDao")  //按照 类型 为 UserDao的bean实例注入    

这里就只做个语法的介绍,注解的使用大同小异,按照上方步骤自行测试即可。

3.5 小结

  • 常用的注解有 3 种 :@Autowired @Qualifier @Resources
  • 注解注入的形式两种 : 按照 bean 的 id 注入,或者按照 bean 的类型注入。

ps:以上内容来自对慕课教程的学习与总结

你可能感兴趣的:(Java开发,java,spring,依赖注入)