在讲解Spring的IoC和DI之前,我们先通过一个例子来引出为何要学习使用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();
}
}
控制台输出如下: