Spring学习记录之Spring启示录
Spring学习记录之Spring启示录
前言
这篇文章是我第二次学习b站老杜的spring
相关课程所进行的学习记录
,算是对课程内容及笔记的二次整理,以自己的理解方式进行二次记录,其中理解可能存在错误,欢迎且接受各位大佬们的批评指正;
关于本笔记,只是我对于相关知识遗忘时快速查阅了解使用,至于课程中实际实验配置等,也只是记录关键,并不会记录详细步骤,若想了解可以关注我博客的项目经验模块,我会在实际项目开发过程中总结项目经验,在该模块发布!
学习视频地址:https://www.bilibili.com/video/BV1Ft4y1g7Fb/
视频配套笔记:https://www.yuque.com/dujubin/ltckqu/kipzgd?singleDoc# 《Spring6》 密码:mg9b
目录
Spring启示录
一、阅读以下代码
UserController
public class UserController {
private UserService userService = new UserServiceImpl();
public void login(){
String username = "admin";
String password = "123456";
boolean success = userService.login(username, password);
if (success) {
// 登录成功
} else {
// 登录失败
}
}
}
UserService
省略...
UserServiceImpl
public class UserServiceImpl implements UserService {
private UserDao userDao = new UserDaoImplForMySQL();
public boolean login(String username, String password) {
User user = userDao.selectByUsernameAndPassword(username, password);
if (user != null) {
return true;
}
return false;
}
}
UserDao
省略...
UserDaoImplForMySQL
public class UserDaoImplForMySQL implements UserDao {
public User selectByUsernameAndPassword(String username, String password) {
// 连接MySQL数据库,根据用户名和密码查询用户信息
return null;
}
}
二、分析上述代码存在的问题
如果用户表从MySql迁移到了Oracle(模拟开发需求变更)
我们需要再提供一个UserDao
实现类UserDaoImplForOracle
public class UserDaoImplForOracle implements UserDao {
public User selectByUsernameAndPassword(String username, String password) {
// 连接Oracle数据库,根据用户名和密码查询用户信息
return null;
}
}
做完这一步之后,又因为我们在UserServiceImpl
中引入了UserDaoImplForMySQL
实现类对象,我们也需要切换其为UserDaoImplForOracle
实现类对象。
很明显,以上的开发需求只是进行功能的扩展,添加了一个新的类UserDaoImplForOracle
来应付数据库的变化(这样的操作在实际开发中是很常见的),但是对功能扩展的过程中引起了类间的连锁反应,这样的代码是耦合的、不易维护的。
其实这里有了解过面向对象编程的六大原则同学,也能看出这里违反了OCP开闭原则和DIP依赖倒置原则。
三、引出三种编程思想
这里借老杜的解释再阐述一下:
① OCP开闭原则
开闭原则是这样说的:在软件开发过程中应当对扩展开放,对修改关闭。也就是说,如果在进行功能扩展的时候,添加额外的类是没问题的,但因为功能扩展而修改之前运行正常的程序,这是忌讳的,不被允许的。因为一旦修改之前运行正常的程序,就会导致项目整体要进行全方位的重新测试。这是相当麻烦的过程。导致以上问题的主要原因是:代码和代码之间的耦合度太高。如下图所示:
可以很明显的看出,上层是依赖下层的。UserController
依赖UserServiceImpl
,而UserServiceImpl
依赖UserDaoImplForMySQL
,这样就会导致下面只要改动,上面必然会受牵连(跟着也会改),所谓牵一发而动全身。这样也就同时违背了另一个开发原则:依赖倒置原则。
② DIP依赖倒置原则
依赖倒置原则(Dependence Inversion Principle),简称DIP,主要倡导面向抽象编程,面向接口编程,不要面向具体编程,让上层不再依赖下层,下面改动了,上面的代码不会受到牵连。这样可以大大降低程序的耦合度,耦合度低了,扩展力就强了,同时代码复用性也会增强。(软件七大开发原则都是在为解耦合服务)
你可能会说,上面的代码已经面向接口编程了呀:
确实已经面向接口编程了,但对象的创建是:new UserDaoImplForOracle()
显然并没有完全面向接口编程,还是使用到了具体的接口实现类。什么叫做完全面向接口编程?什么叫做完全符合依赖倒置原则呢?请看以下代码:
如果代码是这样编写的,才算是完全面向接口编程,才符合依赖倒置原则。那你可能会问,这样userDao是null,在执行的时候就会出现空指针异常呀。你说的有道理,确实是这样的,所以我们要解决这个问题。解决空指针异常的问题,其实就是解决两个核心的问题:
- 第一个问题:谁来负责对象的创建。【也就是说谁来:new UserDaoImplForOracle()/new UserDaoImplForMySQL()】
- 第二个问题:谁来负责把创建的对象赋到这个属性上。【也就是说谁来把上面创建的对象赋给userDao属性】
如果我们把以上两个核心问题解决了,就可以做到既符合OCP开闭原则,又符合依赖倒置原则。
很荣幸的通知你:Spring框架可以做到。
在Spring框架中,它可以帮助我们new对象,并且它还可以将new出来的对象赋到属性上。换句话说,Spring框架可以帮助我们创建对象,并且可以帮助我们维护对象和对象之间的关系。比如:
Spring可以new出来UserDaoImplForMySQL对象,也可以new出来UserDaoImplForOracle对象,并且还可以让new出来的dao对象和service对象产生关系(产生关系其实本质上就是给属性赋值)。
很显然,这种方式是将对象的创建权/管理权交出去了,不再使用硬编码的方式了。同时也把对象关系的管理权交出去了,也不再使用硬编码的方式了。像这种把对象的创建权交出去,把对象关系的管理权交出去,被称为控制反转。
③ IoC控制反转
控制反转(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计思想,可以用来降低代码之间的耦合度,符合依赖倒置原则。
控制反转的核心是:将对象的创建权交出去,将对象和对象之间关系的管理权交出去,由第三方容器来负责创建与维护。即不在程序中采用硬编码的方式来new对象了(把new对象的权力交出去),同时也不在程序中采用硬编码的方式来维护对象之间的关系了(对象之间关系的维护权也交出去了)。
控制反转常见的实现方式:依赖注入(Dependency Injection,简称DI)(补充强调:IoC是面向对象编程中的一种设计思想,DI是实现这种思想的一种方式。)
个人补充:在上述示例中我们讲到,不在类中自己去new对象,去使用该对象引用时一定会报空指针异常。所以我们要在使用该对象时让Spring自动将需要的对象实例赋值给改引用。一般情况下,我们给类中对象属性赋值时,一般采用两种方式,即一是通过构造方法给改对象引用赋值,二是调用该对象实例的set方法给对象引用赋值。所以Spring其实也是通过这两种方式去操作的。
通常,依赖注入的实现又包括两种方式:
-
set方法注入
-
构造方法注入
而Spring框架就是一个实现了IoC思想的框架。
IoC可以认为是一种全新的设计模式,但是理论和时间成熟相对较晚,并没有包含在GoF中。(GoF指的是23种设计模式)
四、如何理解"依赖注入"
依赖注入中"依赖"是什么意思?
依赖其实就是对象和对象之间的关系。A<=>B<=>C
依赖注入中"注入"是什么意思?
注入是一种手段,通过这种手段,可以让对象之间产生依赖关系。
五、总结
到这里,我们就引出了学习Spring大概我们要解决的问题是什么,以及为什么要学Spring。很显然接下来我们就要学习哪些方法手段(代码写法)可以把对象的创建权交出去,把对象关系的管理权交出去,实现IoC控制反转。