【Spring框架】IOC控制反转与DI依赖注入,实例分析与测试

IOC控制反转

什么是IOC?

IOC(Inverse Of Controll),意为控制反转,也就是将创建对象的权限交给Spring工厂,让Spring工厂来创建对象,解决了具有依赖关系的组件之间的强耦合,使得项目形态更加稳健

IOC容器

Spring 提供了两种 IOC 容器,分别为 BeanFactory 和 ApplicationContext

BeanFactory

BeanFactory 是基础类型的 IoC 容器,它由 org.springframework.beans.facytory.BeanFactory 接口定义,并提供了完整的 IoC 服务支持。简单来说,BeanFactory 就是一个管理 Bean 的工厂,它主要负责初始化各种 Bean,并调用它们的生命周期方法。

ApplicationContext

ApplicationContext 是 BeanFactory 的子接口,也被称为 Spring 上下文。该接口的全路径为 org.springframework.context.ApplicationContext,它不仅提供了 BeanFactory 的所有功能,还添加了对 i18n(国际化)、资源访问、事件传播等方面的良好支持。

实例测试

1、创建Maven项目

2、在pom.xml中导入依赖

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>4.1.6.RELEASE</version>
</dependency>

3、在resources目录下创建配置文件:applicationContext.xml

<?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">

</beans>

4、创建接口com.robot.dao.UserDao和实现类com.robot.dao.impl.UserDaoImpl

5、配置applicationContext.xml文件,在beans标签中添加bean标签,每一个bean都是一个对象,id和name可以自己随意起,class为实现类,不能为接口,因为接口不能实例化

<bean id="userDao" name="userDao" class="com.robot.dao.impl.UserDaoImpl"></bean>

6、编写测试类,测试spring来创建对象

  • 三种方式来创建对象
public class UserDaoTest {
    @Test
    public void test() {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");

        // 根据id获得对象
        UserDao userDao1 = (UserDao) applicationContext.getBean("userDao");
        // 根据name获得对象
        UserDao userDao2 = (UserDao) applicationContext.getBean("userDao");
        // 根据class获得对象
        UserDao userDao3 = applicationContext.getBean(UserDao.class);

        System.out.println(userDao1);
        System.out.println(userDao2);
        System.out.println(userDao3);
    }
}

执行测试类,打印结果输出三个对象,则代表创建成功

DI依赖注入

在Spring创建对象的同时,为其属性赋值,称之为依赖注入

setter方法注入

在配置好环境后,测试依赖注入

1、构建dao:UserDao和UserDaoImpl,并实现一个方法

public class UserDaoImpl implements UserDao {
    public int insertUser() {
        System.out.println("UserDaoImpl::insertUser()");
        return 0;
    }
}

2、构建service:UserService和UserServiceImpl,并实现一个方法,在方法中调用dao层对象

  • 在以前会使用 UserDao userDao = new UserDaoImpl(); 的方式去创建一个dao层对象,但这样实现方式耦合度太高,因为一个接口可能会对应多个实现类,当需要换一个实现类的时候,那么service层也需要更改,所以耦合度太高,需要解耦
  • 现在使用spring的依赖注入来实现(setter方法实现注入),会自动根据配置文件实例化对应的实现类
public class UserServiceImpl implements UserService {

    UserDao userDao;

    // 使用setter方法注入
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    public int insertUser() {
        return userDao.insertUser();
    }
}

3、配置applicationContext.xml文件

  • 上面是userDao的bean,下面是userService的bean,其中userService的实现类中注入了userDao对象,所以需要添加property标签
  • 使用setter方法注入,setter方法名为setUserDao,所以标签中的name为set后面的字符串首字母小写,也就是userDao,ref就是上面userDao中bean的id,以此将两者联系起来
<bean id="userDao" name="userDao" class="com.robot.dao.impl.UserDaoImpl"></bean>

<bean id="userService" name="userService" class="com.robot.service.impl.UserServiceImpl">
    <property name="userDao" ref="userDao"></property>
</bean>

4、最后在测试类中进行测试,调用userService的方法

public class UserDaoTest {
    @Test
    public void test() {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");

        UserService userService = (UserService) applicationContext.getBean("userService");
        userService.insertUser();
    }
}

结果显示

UserDaoImpl::insertUser()

此条输出结果是dao层方法的输出结果,所以表示成功的将userDaoImpl注入到了userServiceImpl中,使用的是setter方法注入的

构造方法注入

单个参数

只有一个属性需要赋值,构造方法中只有一个参数

1、将service层中的UserServiceImpl方法中的setter方法去掉,添加带参的构造方法,将属性userDao注入

public class UserServiceImpl implements UserService {

    // 原始方式
    // UserDao userDao = new UserDaoImpl();
    UserDao userDao;

    // 使用setter方法注入
//    public void setUserDao(UserDao userDao) {
//        this.userDao = userDao;
//    }

    // 使用构造方法注入
    public UserServiceImpl(UserDao userDao) {
        this.userDao = userDao;
    }

    public int insertUser() {
        return userDao.insertUser();
    }
}

2、修改applicationContext.xml配置文件

  • 其中constructor-arg标签中的ref属性值为要注入的属性的id,也就是上面dao层bean的id
<?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="userDao" name="userDao" class="com.robot.dao.impl.UserDaoImpl"></bean>

<!--    setter方法注入-->
<!--    <bean id="userService" name="userService" class="com.robot.service.impl.UserServiceImpl">-->
<!--        <property name="userDao" ref="userDao"></property>-->
<!--    </bean>-->

<!--    构造方法注入-->
    <bean id="userService" class="com.robot.service.impl.UserServiceImpl">
        <constructor-arg ref="userDao"/>
    </bean>

</beans>

3、测试

  • 只需要修改上面两个地方即可,然后即可测试
  • 最终结果还是和上面输出的结果相同,都输出了 UserDaoImpl::insertUser(),表明注入成功

多个参数

当只有一个参数的时候,可以直接注入,但是当有多个属性需要注入的时候,那么构造方法的多个参数就可能发生歧义

举个栗子

在service层的实现类中需要注入多个属性

1、再创建dao层的一个GoodsDao接口和GoodsDaoImpl实现类,并实现方法

public class GoodsDaoImpl implements GoodsDao {
    public int deleteGoods() {
        System.out.println("GoodsDaoImpl::deleteGoods()");
        return 0;
    }
}

2、创建service层的GoodsService接口和GoodsServiceImpl实现类,并实现方法

  • 使用构造方法注入两个属性,构造方法中的这两个参数的类型不同
public class GoodsServiceImpl implements GoodsService {
    // 需要注入的两个属性
    UserDao userDao;
    GoodsDao goodsDao;

    // 构造方法注入
    public GoodsServiceImpl(UserDao userDao, GoodsDao goodsDao) {
        this.userDao = userDao;
        this.goodsDao = goodsDao;
    }

    public int deleteGoods() {
        userDao.insertUser();
        goodsDao.deleteGoods();
        return 0;
    }
}

3、配置applicationContext.xml配置文件

  • 其中接着上面配置的添加了goodsDao的bean和goodsService的bean
  • 因为构造方法中两个参数的类型不同,所以在配置goodsService的时候,constructor-arg标签内需要添加type属性,来显示的指定参数的类型,从而定位到具体的参数
  • (如果参数类型相同,则无需使用type属性)
<?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="userDao" name="userDao" class="com.robot.dao.impl.UserDaoImpl"></bean>
    <bean id="goodsDao" class="com.robot.dao.impl.GoodsDaoImpl"></bean>

<!--    setter方法注入-->
<!--    <bean id="userService" name="userService" class="com.robot.service.impl.UserServiceImpl">-->
<!--        <property name="userDao" ref="userDao"></property>-->
<!--    </bean>-->

<!--    构造方法注入-->
    <bean id="userService" class="com.robot.service.impl.UserServiceImpl">
        <constructor-arg ref="userDao"/>
    </bean>

<!--    多个参数时的构造方法注入 type-->
    <bean id="goodsService" class="com.robot.service.impl.GoodsServiceImpl">
        <constructor-arg type="com.robot.dao.UserDao" ref="userDao"></constructor-arg>
        <constructor-arg type="com.robot.dao.GoodsDao" ref="goodsDao"></constructor-arg>
    </bean>

</beans>

除了使用type属性来指定参数,还可以使用index索引,来指定参数,和上面的效果一样,使用其中一个方法即可

<!--    多个参数时的构造方法注入 index-->
<bean id="goodsService" class="com.robot.service.impl.GoodsServiceImpl">
    <constructor-arg index="0"  ref="userDao"></constructor-arg>
    <constructor-arg index="1" ref="goodsDao"></constructor-arg>
</bean>

4、测试

public class GoodsDaoTest {
    @Test
    public void deleteTest() {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        GoodsService goodsService = (GoodsService) applicationContext.getBean("goodsService");
        goodsService.deleteGoods();
    }
}

结果为输出两条信息

UserDaoImpl::insertUser()
GoodsDaoImpl::deleteGoods()

因为在GoodsServiceImpl中注入了这两个属性,然后调用了这两个属性的方法,所以输出两条结果信息,说明注入成功

自动注入

不需要写setter方法,也不需要写构造方法,只需要在配置文件中配置即可,两种方式

1、基于类型自动注入值

<bean id="userService" class="com.robot.service.impl.UserServiceImpl" autowire="byType"></bean>

2、基于名字自动注入值

<bean id="userService" class="com.robot.service.impl.UserServiceImpl" autowire="byName"></bean>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值