Spring控制反转(IoC)和依赖注入(DI)

       在讲解Spring的IoCDI之前,我们先通过一个例子来引出为何要学习使用Spring的控制反转依赖注入

有两个类,一个是A、一个是B,如下:

public class A{
    public void m1(){}
}

public class B{
    public void m1();
}

A类和B类都有相同的方法m1

现在我们调用B类的m1方法完成一些事情,而B类中的m1方法要调用A类中的m1方法才可以完成这个事情,好像有点绕?好吧,看下面代码清晰一点:

public class B{

    private A a; // 1

    public B(){
        this.a = new A(); //2
    }

    public void m1(){
        this.a.m1(); //3
    }

}

分析一下上面代码:
注释1:B类中声明了一个A类型的属性a
注释2:new了一个A类的对象,赋给了a属性
注释3:B类中的m1方法中去调用a.m1()完成业务操作

为了便于理解,先说一下什么是依赖?
       依赖是类与类之间多种关系中的其中一种,类之间的关系还包括继承实现聚合组合关联,当a对象完成某些操作需要调用b对象中的方法来实现时,说明a依赖于对象b,a和b是依赖关系。上面代码中B的m1需要调用A的m1方法,说明了B依赖于A

回归正题,细品上面的代码会发现一些问题,B类中a对象的创建被写死在B的构造方法中了,如果我们想在创建不同的B对象的时候,使用不同的a对象,此时是无能为力的;代码也不利于测试,由于B中a的创建被写死在构造方法中了,我们想测试一下B中不同a对象的效果,此时只能去修改B中的构造方法。

上面代码需要优化,B中a对象的创建不能写死,可以让外部传入进去,调整一下变成了下面这样:

public class B{

    private A a;

    public B(A a){
        this.a = a;
    }

    public void m1(){
        this.a.m1(); 
    }
}

上面代码可以在创建B对象的时候,将外部创建好的a对象传入进去,创建B对象如下:

A a = new A();
B b = new B(a);
b.m1();

完美?不不不,如果B类中还需要依赖很多类似于A的对象,比如需要依赖于C、D、E、F或者更多对象,首先是需要调整B的构造方法,修改老的构造方法不是很好,可以在B中新增一些构造方法,但是使用B完成一些事情时变成了如下:

A a = new A();
C c = new C();
D d = new D();
E e = new E();
F f = new F();
...
//B说我受不了了...不想再胖下去了...
B b = new B(a,c,d,e,f,...);
b.m1();

       使用者创建B对象之前,需要先将B依赖的对象都给创建好,然后B依赖的这些对象传递给B对象,如果有很多地方都需要用到B类型的对象,都采用这种new的写法,代码量比较大,也不方便维护,如果B中新增了依赖,又需采用new的方式先创建好被依赖的对象,然后将被依赖的对象填充给B对象。

       上面创建对象之前,需要先将被依赖对象(被调用者)通过new的方式创建好,然后将其传递给B,这些工作都是B的调用者自己去做的,所有对象的创建都是由使用者自己去控制的,弊端上面也说了,代码量也比较大,代码耦合度比较高(依赖有调整,改动也比较大),也不利于扩展。B说自己太累了,于是找了第三者帮自己去创建需要依赖的对象,而这个第三者便是Spring容器。

接下来,让我们展示一下Spring容器的IoC和DI,感受其强大之处吧。

  • Spring的控制反转IoC
    在使用Spring框架之后,从调用者的角度对象的实例不再由调用者来创建,而是由Spring容器来创建的,Spring容器会负责控制程序之间的关系,而不是由调用者的程序代码直接控制。这样,控制权由应用代码转移到了Spring容器,控制权发生了反转,这就是Spring的控制反转。
    在这里插入图片描述

  • Spring的依赖注入DI
    在使用Spring框架之后,从Spring容器的角度看Spring容器负责将被依赖对象赋值给调用者的成员变量,这相当于为调用者注入了它依赖的实例,这就是Spring的依赖注入。
    在这里插入图片描述
    从上面的解释看出,依赖注入与控制反转的含义相同,只不过这两个称呼是从两个角度描述的同一概念

代码实例实现请往下看:

创建接口UserDao,在接口中定义一个say()方法

package com.itheima.ioc;
public interface UserDao {
	public void say();
}

创建UserDao接口的实现类UserDaoImpl,该类需要实现接口中的say()方法,并输出一条语句

package com.itheima.ioc;
public class UserDaoImpl implements UserDao {
	public void say() {
		System.out.println("userDao say hello World !");
	}
}

创建Spring的配置文件application.xml,并在配置文件中创建一个id为userDao的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-4.3.xsd"> 
	<!-- 将指定类配置给Spring,让Spring创建其对象的实例 -->
	<bean id="userDao" class="com.itheima.ioc.UserDaoImpl" />
</beans>

创建测试类TestIoC

package com.itheima.ioc;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestIoC {
	public static void main(String[] args) {
		//1.初始化spring容器,加载配置文件
		ApplicationContext applicationContext = 
		    new ClassPathXmlApplicationContext("applicationContext.xml");
		//2.通过容器获取userDao实例
		UserDao userDao = (UserDao) applicationContext.getBean("userDao");
		//3.调用实例中的say()方法
		userDao.say();
	}
}

控制台输出如下:
在这里插入图片描述
接下来写一下依赖注入的实现:

创建UserService接口,在接口中添加一个say()方法

package com.itheima.ioc;
public interface UserService {
	public void say();
}

创建UserService接口的实现类UserServiceImpl,在类中声明userDao属性,并添加属性setter方法

package com.itheima.ioc;
public class UserServiceImpl implements UserService {
	// 声明UserDao属性
	private UserDao userDao;
	// 添加UserDao属性的setter方法,用于实现依赖注入
	public void setUserDao(UserDao userDao) {
		this.userDao = userDao;
	}
	// 实现的接口中方法
	public void say() {
		//调用userDao中的say()方法,并执行输出语句
		this.userDao.say();
		System.out.println("userService say hello World !");
	}
}

在配置文件applicationContext.xml中,创建一个id为userService的Bean,该Bean用于实例化UserServiceImpl类的信息,并将userDao的实例注入到userService中

	<!--添加一个id为userService的实例 -->
	<bean id="userService" class="com.itheima.ioc.UserServiceImpl">
		<!-- 将id为userDao的Bean实例注入到userService实例中 -->
	   	<property name="userDao" ref="userDao" />
	</bean>

编写测试类TestDI

package com.itheima.ioc;
import org.springframework.context.ApplicationContext;
import 
    org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestDI {
	public static void main(String[] args) {
		//1.初始化spring容器,加载配置文件
		ApplicationContext applicationContext = 
             new ClassPathXmlApplicationContext("applicationContext.xml");
		//2.通过容器获取UserService实例
		UserService userService = 
            (UserService) applicationContext.getBean("userService");
		//3.调用实例中的say()方法
		userService.say();
	}
}

控制台输出如下:
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_43894879/article/details/105870000