本篇文章记录了我个人对Spring框架能够解耦这一优点的理解,将它分享出来和大家一起学习交流,同时也欢迎大家纠错改正
相信大家对 Spring 的 IOC 和 DI 并不陌生了,它们为 Spring 框架带来了方便解耦这一优点。我在学习完Spring框架的IOC 和 DI 之后的初期阶段,对它们的概念和用法有了基础性的认识和掌握。
若对这两个知识有遗忘请戳:
我的文章:https://blog.csdn.net/weixin_41866960/article/details/83930213
大牛的文章(很棒):https://blog.csdn.net/bestone0213/article/details/47424255
然后,问题来了,我发现好像这个 Spring 的 IOC 带来的不同就是无非就是从以前我们自己主动去 new 一个对象,变成了 IOC 容器向对象进行注入,控制权发生了变化,那为什么说这样就方便了解耦了呢?我查阅了多篇文章。。。总算是有了一个浅层的理解,下面为大家一 一道来。
提起 “解耦”,大家可能会联想到 “高内聚,低耦合”,那再简单回顾一下什么是内聚什么是耦合吧。。
高内聚,从字面上来看有聚精会神的意思,也就是尽可能的使一个模块或一个类再或者是一个方法只专注做好一件事。
低耦合,从字面上来看有藕断丝连的意思,也就是尽可能的使每个模块之间或者每个类之间再或者是每个方法之间的关联关系减少,这样可以使各自尽可能的独立,一个地方出错,不会影响全部,更能提高代码的重用性。
“高内聚,低耦合”,用一句话概括就是写的代码尽可能专一的完成一个任务,且各段代码尽量模块化互相独立。
尊重原创,下面这段解释有的地方我参考这位作者的文章:https://blog.csdn.net/sinat_26422099/article/details/78437652
我们都知道,Java的程序设计是面向对象的,那么意味着使用Java语言设计出来的程序,底层会有N多个类,各个类之间相互协作,实现业务逻辑。如果把对象比喻成齿轮,就像下面这样的,一个齿轮能带动另一个齿轮运作。但是这样齿轮和齿轮之间的交互,便可以简单理解为他们之间产生了耦合的关系。
这里,我们假设 齿轮A 的运作,在一定前提下,它必须靠着 齿轮B 和 齿轮C 才能运作,那么,意味着 齿轮A 对 齿轮B 和齿轮C产生了“依赖” ,它们之间是一个 依赖关系。伴随着工业级应用的规模越来越庞大,对象之间的依赖关系也越来越复杂,经常会出现对象之间的多重依赖性关系,因此,架构师和设计师对于系统的分析和设计,将面临更大的挑战。对象之间耦合度过高的系统,必然会出现牵一发而动全身的情形。
图象还不够生动~?结合代码情景来看看
曾经我们在Dao层为了解决更换数据库的难题,我们自己动手写工厂,让工厂去创建从而在客户端调用时隐藏了具体的创建细节,这样我们就可以更换数据库了。但是下面代码例子并没有引入工厂~
首先,我们创建Dao层的接口,并且给出对于不同数据库驱动的不同实现类
public interface UserDao {
public void addUser(String username,String password);
}
Dao层UserDao实现类UserDaoMySqlImpl.java
public class UserDao4MySqlImpl implements UserDao {
@Override
public void addUser(String username, String password) {
System.out.println("UserDao4MySqlImpl.addUser()");
}
}
Dao层UserDao实现类UserDaoOracleImpl.java
public class UserDao4OracleImpl implements UserDao {
@Override
public void addUser(String username, String password) {
System.out.println("UserDao4OracleImpl.addUser()");
}
}
然后,业务逻辑层manager
Manager层接口UserManager.java
public interface UserManager {
public void addUser(Stringusername,String password);
}
Manager层实现类UserManagerImpl.java
public class UserManagerImpl implements UserManager {
@Override
public void addUser(String username, String password) {
//主动创建了实现类
//UserDao userDao = new UserDao4MySqlImpl();
//如果想要更换实现类,就要这样换了
UserDao userDao = new UserDao4OracleImpl();
userDao.addUser(username,password);
}
}
客户端类调用
public class client {
/**
* @param args
*/
public static void main(String[] args) {
//TODO Auto-generated method stub
//服务(对象)定位,由我们的应用程序负责服务(对象)定位.
//因为自己产生了主动的查找.所以必须依赖,并且写死.
UserManager userManager = new UserManagerImpl();
userManager.addUser("张三","123");
}
}
来看看下面这段代码
UserDao userDao = new UserDao4OracleImpl();
UserManagerImpl中这样调用 Dao 层,UserDao userDao = new UserDaoOracleImpl();要实现接口,就必须要执行上面的代码 ,这样一来耦合关系就产生了。ManagerImpl 类与 UserDaoOracleImpl 类就是依赖关系。
UserManager userManager = new UserManagerImpl();
同样,在客户端类中,UserManger 和 UserMangerImpl 也是依赖关系。这样一来,对象之间的多重依赖性关系就形成了。。。同时,代码的耦合也就产生了。我们如果要更换数据库需要打开 UserManagerImpl 类去更改不同的实现,同样在client调用如果要更改不同的业务也需要不同的UserManager实现。
耦合关系不仅会出现在对象与对象之间,也会出现在软件系统的各模块之间,以及软件系统和硬件系统之间。对象之间的耦合是无法避免的,也是必要的,但如何降低系统之间、模块之间和对象之间的耦合度,是软件工程永远追求的目标之一。为了解决对象之间的耦合度过高的问题,软件专家Michael Mattson提出了 IOC 理论,用来实现对象之间的“解耦”。所以,应运而生的便是Spring 的 IOC
IOC理论提出的观点大体是这样的:借助于“第三方”实现具有依赖关系的对象之间的解耦
IOC解耦过程
大家看到了吧,由于引进了中间位置的“第三方”,也就是IOC容器,使得A、B、C、D这4个对象没有了耦合关系,齿轮之间的传动全部依靠“第三方”了,全部对象的控制权全部上缴给“第三方”IOC容器,所以,IOC容器成了整个系统的关键核心,它起到了一种类似“粘合剂”的作用,把系统中的所有对象粘合在一起发挥作用,如果没有这个“粘合剂”,对象与对象之间会彼此失去联系,这就是有人把IOC容器比喻成“粘合剂”的由来。
我们再来做个试验:把上图中间的IOC容器拿掉,然后再来看看这套系统:
我们可以看到,现在各个对象之间已经独立开来了,最大程度上降低了各个对象之间的耦合程度。当你在实现A的时候,根本无须再去考虑B、C和D的实现方式。所以,如果真能实现IOC容器,对于系统开发而言,这将是一件多么美好的事情,参与开发的每一成员只要实现自己的类就可以了,跟别人没有任何关系! IOC容器有很多种,PicoContainer、JBoss、HiveMind、spring,在这里我们主要说的是Spring的IOC容器。
过去,我们实例化一个接口要这样做
UserDao userDao = new UserDao4OracleImpl();
但现在不再需要我们自动去new,而是交给Spring的IOC容器去做这件事情,他自己(SpringIOC容器)会找,并且根据配置文件找到后new好直接传递给我们。具体做法如下
我们的Dao层代码不变,我们的UserManagerImpl代码如下所示
public class UserManagerImpl implements UserManager {
//这样改看不到具体的实现了. 但是在client调用时看到组装的过程.
private UserDao userDao;
//构造方法进行赋值.
// public UserManagerImpl(UserDao userDao) {
//
// this.userDao = userDao;
// }
//
//通过setter方法来把依赖对象注入。
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void addUser(String username, String password) {
userDao.addUser(username, password);
}
}
这里,setter方法是为了让Spring将UserDao与UserManagerImpl的关系注入(DI)进来,也可以通过构造方法注入。
然后,提供一个ApplicationContext.xml配置文件,这个XML文件就是Spring中用来配置Bean的。通过这样的一个方式可以把分散的Bean组装一个整体系统,放入IOC容器内,再去实现具体的业务了逻辑。Spring就像一个大工厂,对所有Bean的创建和依赖关系进行管理。
?xml version="1.0" encoding="UTF-8"?>
接下来就是测试了
public class client {
public static void main(String[] args) {
//spring中有BeanFactory,是一个接口.
//专门读取applicationContext.xml文件的实现.
//ClassPathXmlApplicationContext类就是对BeanFactory接口的实现.
//会把applicationContext.xml这个文件读进来,并且创建对象.
BeanFactory factory = new ClassPathXmlApplicationContext("applicationContext.xml");
//产品标识.
UserManager userManager = (UserManager)factory.getBean("userManager");
userManager.addUser("张三", "123");
}
}
这样就完成了用SpringIOC容器来解决依赖问题,不需要我们自己去建立工厂,我们只要声明一个接口,具体的实现在配置文件中告诉SpringIOC容器,他会自己找到并进行创建相应的实例,然后传递给我们,本来是由我们自己去控制对象之间的依赖关系,现在由IOC来控制了,这就是控制反转。
看这里,已经不用主动给UserDao new一个实现类了,因为Spring已经帮我们做好了。在UserManagerImpl这个类里面根本不用考虑这个UserDao是用oracle数据库实现还是mysql的数据库实现,只要遵从接口的使用规则便可,实现了模块之间的独立开发。
public class UserManagerImpl implements UserManager {
private UserDao userDao;