java依赖反转例子_Spring IoC控制反转与DI依赖注入

初识Spring,这篇文章我简单的介绍了一些关于Spring的优势和核心概念。这篇文章对Ioc和DI对于初接触Spring的自己来说做个了结

程序的耦合

我们再来谈一谈程序的耦合

I. 程序的耦合:程序间的依赖关系

包括

类之间的依赖

方法间的依赖

II. 为什么会出现程序的耦合呢?

在我写代码时,通常会遵循 '单一性' 的原则。就是在一个类中,尽量保持它功能的单一性,一个类尽量独立实现一个功能,如果很多代码都写到一个类中,出现问题很难更改。但是,复杂的功能逻辑往往需要调用很多方法来实现,通常都需要两个或者更多的类通过彼此的合作来实现业务逻辑。

某个对象需要获取与合作对象的引用,如果这个获取的过程需要自己实现(就是使用new实例化对象),代码的耦合度就会高,维护起来的成本就比较高。

解耦:降低程序间的依赖关系

实际开发中应该做到:编译期不依赖,运行时才依赖。

解耦的思路

第一步:使用反射来创建对象,而避免使用new关键字

第二步:通过读取配置文件来获取要创建的对象全限定类名

2. 控制反转IoC

控制反转理解

我们来通过实例来模拟一下。假如老Q是计算机院的辅导员,他想让数媒专业的班级去听讲座(大一大二的你是不是常常被拉去凑人数,讲座听半天听不懂就瞌睡了),代码可以这样实现:

数媒班级代码如下所示:

public class Media {

public void liaten() {

System.out.println("数媒专业在听讲座!");

}

}

老王类的代码如下所示:

public class LaoQ {

public void advice() {

//通知数媒班级听讲座

new Media().liaten();

}

}

测试类的代码如下所示:

public class Test {

public static void main(String\[\] args) {

LaoQ laoQ = new LaoQ();

laoQ.advice();

}

}

运行结果:

数媒专业在听讲座!

LaoQ 类的 advice()方法中使用 new 关键字创建了一个 Media类的对象——这种代码的耦合度就很高,维护起来的成本就很高,为什么这么说呢?

某一天,学校又来学者作报告了,老Q想起了数媒专业(俺的万精油专业),可数媒专业去上实验了,让谁去听呢,老Q辅导员想起了软工专业,于是 LaoQ 类就不得不重新通知了,于是代码变成了这样:

public class Soft {

public void listen() {

System.out.println("软工专业在听讲座!");

}

}

public class LaoQ {

public void advice() {

//通知数媒专业听讲座

new Media().liaten();

}

public void advice1() {

//通知软工专业听讲座

new Media().liaten();

}

}

假如软工专业去实习了,老Q辅导员打算要让计科专业去听讲座。这样下去的话,LaoQ这个类里面都是强耦合了。

老Q最为一个辅导员,这种打酱油的工作,还用得着亲自负责么!于是,他找了一个办公室助理李同学来帮他干这件事。

李同学负责去叫学院班级去执行老Q听讲座的命令。代码可以这样实现:

定义一个听讲座专业的接口,代码如下所示:

public interface Major {

//该班级听讲座

void listen();

}

数媒类的代码修改如下所示:

public class Media implements Major {

@Override

public void listen() {

System.out.println("数媒专业去听讲座!");

}

public boolean experiment() {

// 星期三的时候数媒专业要做实验

return false;

}

}

软工类的代码修改如下所示:

public class Soft implements Major {

@Override

public void listen() {

System.out.println("软工专业听讲座!");

}

}

李同学类的代码如下所示:

public class StuLi {

public static Major getListen() {

Media media = new Media();

if (media.experiment()) {

//如果数媒有实验就通知软工去

return new Soft();

}

return media;

}

}

如果数媒专业在上实验,就通知软工专业去

这时老Q就轻松了,有啥事告诉李同学,自己不再关心到底哪个专业去了,具体安排由李同学通知。

老Q类的代码修改如下:

public class LaoQ {

public void advice() {

//通知数媒专业听讲座

new Media().listen();

}

public void advice1() {

System.err.println("数媒专业在做实验...");

//通知软工专业听讲座

new Soft().listen();

}

}

测试类的代码不变:

public class Test {

public static void main(String\[\] args) {

LaoQ laoQ = new LaoQ();

laoQ.advice();

}

}

数媒专业在做实验... 软工专业听讲座!

我们替老Q想的这个办法就叫控制反转(Inversion of Control,缩写为 IoC),它不是一种技术,而是一种思想——指导我们设计出松耦合的程序。

控制反转从词义上可以拆分为“控制”和“反转”,说到控制,就必须找出主语和宾语,谁控制了谁;说到反转,就必须知道正转是什么。

在紧耦合的情况下,老Q下命令的时候自己要通过new关键字创建依赖的对象(数媒专业和软工专业);而控制反转后,老Q要找听讲座的班级由李同学负责,也就是说控制权交给了李同学,是不是反转了呢?

Q0:IoC的作用是什么?

削弱类之间的耦合(不能完全消除)

Q1:谁控制谁?为什么叫反转?

答:IoC容器控制,而以前是应用程序控制,所以叫反转

Q2:控制什么?

答:控制应用程序所需要的资源(对象、文件……)

Q3:为什么控制?

答:解耦组件之间的关系,削减类之间的耦合度

Q4:控制的哪些方面被反转了?

答:程序的控制权发生了反转,从应用程序转移到了IoC容器

控制反转实例

模拟编写持久层UserDao接口及其实现类

UserDao

public interface UserDao {

//用户注册

void regist();

}

UserDaoImpl

public class UserDaoImpl implements UserDao {

public void regist() {

System.out.println("user regist...");

}

}

模拟编写业务逻辑层UserService接口及其实现类

UserService

public interface UserService {

//用户注册

void regist();

}

UserServiceImpl

public class UserServiceImpl implements UserService {

private UserDao userDao;

public UserServiceImpl() {

System.err.println("对象创建了!");

}

public void regist() {

userDao.regist();

}

}

现在,我们不直接用new来实例化对象了,用上面的例子来说,就是老Q不再具体通知班级了,交由李同学来管理。李同学就相当于Spring管理资源.

我们要做的事在配置文件中配置UserDao和UserService

applicationContext.xml

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

80c8589589e828c56b6732b954a1fdeb.png

模拟编写表现层RegistClient

RegistClient

public class RegistClient {

public static void main(String\[\] args) {

//1.获取核心容器对象

ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");

//2.根据id获取Bean对象

UserDao userDao = ac.getBean("userDao",UserDao.class); //反射拿到字节码强转

UserService userService = (UserService) ac.getBean("userService"); //Object类型

userDao.regist();

}

}

690038fd62aad372fd930001381957ee.png

核心容器的两个接口

获取spring的Ioc核心容器创建对象的策略

ApplicationContext: 单例对象适用

它在构建核心容器时,创建对象采取的策略是采用立即加载的方式。也就是说,只要一读取完配置文件马上就创建配置文件中配置的对象。

BeanFactory: 多例对象使用

它在构建核心容器时,创建对象采取的策略是采用延迟加载的方式。也就是说,什么时候根据id获取对象了,什么时候才真正的创建对象。

ApplicationContext的三个常用实现类:

ClassPathXmlApplicationContext:它可以加载类路径下的配置文件,要求配置文件必须在类路径下。不在的话,加载不了。(更常用)

FileSystemXmlApplicationContext:它可以加载磁盘任意路径下的配置文件(必须有访问权限)

AnnotationConfigApplicationContext:它是用于读取注解创建容器的

获取Bean对象

由于是Spring来管理我们对象的产生,不知道传入的Bean对象是什么类型的,所以有两种方法来获取Bean对象

Object类(需要强转)

反射获取

3 Bean对象

JavaBean,Spring Bean对象的理解

JavaBean,Spring Bean对象的理解

4. 依赖注入DI

依赖注入(Dependency Injection,简称 DI)是实现控制反转的主要方式。它是Spring框架核心IoC的具体实现。

我们的程序在编写时,通过控制反转(Ioc),把对象的创建交给了Spring,但是代码中不可能出现没有依赖的情况。IoC解耦只是降低他们的依赖关系,但不会消除。

例如:我们在业务层仍会调用持久层的方法。 那这种业务层和持久层的依赖关系,在使用Spring之后,就让Spring来维护了。 简单的说,就是坐等框架把持久层对象传入业务层,而不用我们自己去获取。

在当前类需要用到其他类的对象,由Spring为我们提供,我们只需要在配置文件中说明。

在类A的实例创建过程中就创建了依赖的B对象,通过类型或名称来判断将不同的对象注入到不同的属性中。

【注入DI的方式】

大概有 3 种具体的实现形式:

构造函数注入

Setter()注入

使用注解提供

【注入的数据】

能注入的数据:有三类

基本类型和String

其他Bean类型(在配置文件中或者注解配置过的bean)

复杂类型/集合类型

构造函数注入

使用的标签: constructor-arg

标签出现的位置:bean标签的内部

标签中的属性

type:用于指定要注入的数据的数据类型,该数据类型也是构造函数中某个或某些参数的类型

index:用于指定要注入的数据给构造函数中指定索引位置的参数赋值。索引的位置是从0开始

name:用于指定给构造函数中指定名称的参数赋值

value:用于提供基本类型和String类型的数据

ref:用于指定其他的Bean类型数据。它指的就是在Spring的IoC核心容器中出现过的Bean对象(如Date类对象)

9885f71a042d787b4ad69dedc29fa37a.png

构造函数注入注入对比

优势:

在获取bean对象时,注入数据是必须的操作,否则对象无法创建成功

弊端:

改变了bean对象的实例化方式,使我们在创建对象时,如果用不到这些数据,也必须提供

Setter()注入

Setter方法注入(更常用的方式)

涉及的标签:property

出现的位置:bean标签的内部

标签的属性

name:用于指定注入时所调用的set方法名称

value:用于提供基本类型和String类型的数据

ref:用于指定其他的bean类型数据。它指的就是在Spring的IoC核心容器中出现过的bean对象

4957561d34a8b4b20db5239f6ff294b1.png

优势:

创建对象时没有明确的限制,可以直接使用默认构造函数

弊端:

如果有某个成员必须有值,则获取对象时set()方法有可能没有执行

使用注解注入

下篇文章肝吧...

Setter()集合数据注入

对于List、Set、Map、Props集合类型的数据来说,我们使用Sette()来注入

62304bddfb1dd93155f1d9800f034b89.png

结构相同,标签可以互换:

用于给List结构集合注入的标签

list、 arr、 set

用于个Map结构集合注入的标签:

map、props

好啦,Spring的解耦思想,IoC,Bean,DI就介绍到此啦。下一篇讲注解

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值