Spring | IoC理解

一、基本思想

控制反转IoC(Inversion of Control),是一种设计思想,而依赖注入(Dependency Injection,DI)是实现IoC的一种方法,也有人认为DI仅仅是IoC的另一种说法。IoC思想是为了降低程序代码之间的耦合度。

假设我们使用三层架构实现一个用户登录的功能,包括了下面这些类:

  • pojo包

  1. User实体类(定义了用户的姓名、密码等字段)

  • dao包

  1. UserDao接口(定义了一些基本的数据库增删改查操作)

  1. UserDaoImpl实现类(实现了UserDao接口的方法)

  • service包

  1. UserService接口(定义了一些与用户有关的操作,如登录,注册等,这些操作要用到基本的数据库增删改查操作)

  1. UserServiceImpl实现类(实现了UserService接口的方法)

  • test包

  1. Test类(由于测试UserServiceImpl中的方法)

我们在编写UserServiceImpl实现类的时候,需要用到UserDao接口的相关数据库方法。如果按照一般的写法,这里我们会创建一个UserDaoImpl对象,然后去使用这个对象中的方法。即下面的代码:

    private UserDao userDao = new UserDaoImpl();
    ... ... 
    @Override
    public void login() {
        ... ...
        userDao.find();
    }

    @Override
    public void register() {
        ... ...
        userDao.insert();
        userDao.find();
    }

这样写的话,代码耦合度高,存在一些问题:

  • 如果有不同的userDao 实现类,比如使用了MySql数据库的,使用了SQLite数据库的。这样如果要去修改的话,我们需要不断的修改源代码中的内容。

  • 由于UserServiceImpl依赖于UserDaoImpl,如果两个类是由不同的团队开发的,则UserServiceImpl团队不能先工作,因为在UserDaoImpl完成之前,UserServiceImpl是无法通过编译。

为了解决耦合度高问题,我们使用依赖注入的思想:

在编写UserServiceImpl实现类的时候,我们不再是new一个UserDaoImpl对象,而是使用构造器/setter方法,通过参数的方式导入(注入)这个对象。

这样,本来是UserServiceImpl应用内部应该创建及维护的工作,被转义到调用这个类的外部类。对象创建的控制权就由应用内转移到了外部容器,控制权的转移就是所谓的反转,这就是控制反转的基本思想。

public class UserServiceImpl implements UserService { 
   private UserDao userDao;

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

    // 通过 setter方法 注入 userDao
    public void setUserDao(UserDao userDao){
        this.userDao = userDao;
    }
    ... ...
}
public class AppTest {
    ... ...
    @Test
    public void test(){
        UserDao userDao = new UserDaoImpl();
        // userDao对象创建的控制权由UserServiceImpl类转移到AppTest测试
        UserService userService = new UserServiceImpl(userDao);
        userService.login();
        userService.register();
    }
    ... ...
}

二、Spring中的使用

在Spring中,我们把创建UserServiceImpl对象、UserServiceImpl导入UserDao的工作交给IoC容器去做,即下面两段代码:

UserDao userDao = new UserDaoImpl();
UserService userService = new UserServiceImpl(userDao);

我们只需要在xml配置文件里面写好UserService类的定义UserService类需要注入的UserDao类(即类和类之间的关系依赖)这样当我们需要修改userDao对象时,就只需要在xml配置文件中去修改,而不需要改动UserServiceImpl中的代码。

    ... ...

    <!--IoC容器创建UserDaoImpl对象-->
    <bean id="userDao" class="com.shj.dao.impl.UserDaoImpl"/>

    <!--IoC容器创建UserServiceImpl对象-->
    <bean id="userService" class="com.shj.service.impl.UserServiceImpl">
        <!--调用UserServiceImpl中的setUserDao方法-->
        <property name="userDao" ref="userDao"/>
    </bean>

   ... ...

IoC容器自动地帮助我们创建好类,并注入相关的信息。我们在测试类中去调用IoC容器提供的ApplicationContext接口,通过接口中的getBean()方法就可以获取到我们需要的类并使用。

    @Test
    public void testSetterDI(){
        // 读取app.xml配置文件,获取ApplicationContext接口
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("app.xml");
        // 通过ApplicationContext接口获取UserService对象
        UserService userService = (UserService) applicationContext.getBean("userService");
        // 使用
        userService.login();
        userService.register();
    }

下面是整体的流程图:

附:演示的所有的代码
  • pojo包

  1. User实体类(定义了用户的姓名、密码等字段)

由于没有使用数据库,后面UserDao的方法也只是演示,故没有写User类。

  • dao包

  1. UserDao接口(定义了一些基本的数据库增删改查操作)

package com.app.dao;

public interface UserDao {
    void insert();
    void find();
}
  1. UserDaoImpl实现类(实现了UserDao接口的方法)

package com.app.dao.impl;

import com.app.dao.UserDao;

public class UserDaoImpl implements UserDao {
    @Override
    public void insert() {
        System.out.println("dao类.insert");
    }

    @Override
    public void find() {
        System.out.println("dao类.find");
    }
}

  • service包

  1. UserService接口(定义了一些与用户有关的操作,如登录,注册等,这些操作要用到基本的数据库增删改查操作)

package com.app.service;

public interface UserService {
    void login();
    void register();
}
  1. UserServiceImpl实现类(实现了UserService接口的方法)

package com.app.service.impl;

import com.app.dao.UserDao;
import com.app.service.UserService;


public class UserServiceImpl implements UserService {
    private UserDao userDao;

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

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

    @Override
    public void login() {
        System.out.println("服务实现类.login");
        userDao.find();
    }

    @Override
    public void register() {
        System.out.println("服务实现类.register");
        userDao.insert();
        userDao.find();
    }
}

  • test包

  1. Test类(由于测试UserServiceImpl中的方法)

package com.app.test;

import com.app.service.UserService;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {

    @Test
    public void test(){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("app.xml");
        UserService userService = (UserService) applicationContext.getBean("userService");
        userService.login();
        userService.register();
    }
}

  • 配置文件 app.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">

    <!--1.构造器注入-->
<!--    <bean id="userDao" class="com.app.dao.impl.UserDaoImpl"/>-->
<!--    <bean id="userService" class="com.app.service.impl.UserServiceImpl">-->
<!--        <constructor-arg name="userDao" ref="userDao"/>-->
<!--    </bean>-->

    <!--2.setter方法注入-->
    <bean id="userDao" class="com.app.dao.impl.UserDaoImpl"/>
    <bean id="userService" class="com.app.service.impl.UserServiceImpl">
        <!--调用UserServiceImpl中的setUserDao方法-->
        <property name="userDao" ref="userDao"/>
    </bean>
</beans>

猜你喜欢

转载自blog.csdn.net/sun80760/article/details/128556363
今日推荐