文章目录
Spring6 -(01)案例引出 Spring 概念
Srpring6支持的JDK版本为17
1. 环境准备
1.1 创建工程
1.2 配置JDK版本
File -> Project Structure
1.3 配置Maven环境
File -> Settings -> Build,Execution,Deployment -> Build Tools -> Maven
1.4 创建一个模块
右键工程spring6 -> New -> Module
2. 模拟开发
根据用户的id删除某个用户
2.1 控制层实现
package com.julissa.spring6.controllers;
import com.julissa.spring6.service.UserService;
import com.julissa.spring6.service.impl.UserServiceImpl;
/**
* 应用层
*/
public class UserController {
/**
* 根据id删除用户
*/
public void deleteUserById(int userid) {
UserService userService = new UserServiceImpl();
userService.deleteUserById(userid);
}
}
2.2 业务层实现
package com.julissa.spring6.service.impl;
import com.julissa.spring6.dao.UserDao;
import com.julissa.spring6.dao.impl.UserDaoImplForMySQL;
import com.julissa.spring6.service.UserService;
/**
* 业务层UserService的实现类
*/
public class UserServiceImpl implements UserService {
private UserDao userDao = new UserDaoImplForMySQL();
@Override
public void deleteUserById(int userId) {
userDao.deleteUserById(userId);
}
}
2.3 持久层实现
package com.julissa.spring6.dao.impl;
import com.julissa.spring6.dao.UserDao;
/**
* UserDao的实现类,操作MySQL数据库
* @author Julissa
*/
public class UserDaoImplForMySQL implements UserDao {
@Override
public void deleteUserById(int id) {
System.out.println("---正在连接MySQL数据库---");
System.out.println("删除id为" + id + "的用户");
}
}
2.4 测试类实现
package com.julissa.spring.controllers;
import com.julissa.spring6.controllers.UserController;
import org.junit.jupiter.api.Test;
public class TestUserController {
@Test
public void test(){
UserController userController = new UserController();
userController.deleteUserById(2);
}
}
执行结果:
可以看出,UserDaoImplForMySQL中主要是连接MySQL数据库进行操作。
2.5 对系统进行拓展
如果更换到Oracle数据库上,则需要再提供一个UserDaoImplForOracle
package com.julissa.spring6.dao.impl;
import com.julissa.spring6.dao.UserDao;
/**
* UserDao的实现类,操作Oracle数据库
* @author Julissa
*/
public class UserDaoImplForOracle implements UserDao {
@Override
public void deleteUserById(int id) {
System.out.println("---正在连接Oracle数据库---");
System.out.println("删除id为" + id + "的用户");
}
}
很明显,以上的操作正在进行功能的扩展,添加了一个新的类UserDaoImplForOracle来应付数据库的变化,这里的变化会引起连锁反应吗?
当然会,如果想要切换到Oracle数据库上,UserServiceImpl类代码就需要修改
package com.julissa.spring6.service.impl;
import com.julissa.spring6.dao.UserDao;
import com.julissa.spring6.dao.impl.UserDaoImplForMySQL;
import com.julissa.spring6.dao.impl.UserDaoImplForOracle;
import com.julissa.spring6.service.UserService;
/**
* 业务层UserService的实现类
*/
public class UserServiceImpl implements UserService {
//private UserDao userDao = new UserDaoImplForMySQL();
private UserDao userDao = new UserDaoImplForOracle();
@Override
public void deleteUserById(int userId) {
userDao.deleteUserById(userId);
}
}
对代码进行修改,这就违背了OCP开闭原则,DIP依赖倒置原则
3.软件开发原则
3.1 OCP开闭原则
OCP:是软件七大开发原则中最基本的一个原则:开闭原则
- 对拓展开放
- 对修改关闭
OCP原则是最核心的,最基本的,其他的6个原则都是为这个原则服务的
OCP原则的核心是什么:
- 只要你在拓展系统功能的时候,没有修改以前写好的代码,就是符合OCP原则,反之,如果在拓展系统的时候,修改了之前的代码,那么这个设计就是失败的。
- 这是忌讳的,不被允许的。因为一旦修改之前运行正常的程序,就会导致项目整体要进行全方位的重新测试。这是相当麻烦的过程。导致以上问题的主要原因是:代码和代码之间的耦合度太高。
3.1 DIP依赖倒置原则
上面例子的依赖关系:
上层是依赖下层的。UserController依赖UserServiceImpl,而UserServiceImpl依赖UserDaoImplForMySQL,这样就会导致下面只要改动,上面必然会受牵连(跟着也会改),所谓牵一发而动全身。
什么是违背了依赖倒置原则:
凡是上层依赖下层的,只要“下”一改动,“上”就受到牵连
什么是符合依赖倒置原则:
上层不再依赖下层,
依赖倒置原则的核心:
倡导面向接口编程,面向抽象编程,不要面向具体编程,让上层不再依赖下层,下面改动了,上面的代码不会受到牵连
什么是依赖倒置原则:
面向接口编程,面向抽象编程,不要面向具体编程
依赖倒置原则的目的:
降低程序的耦合度,提高拓展力
举例:
public class UserServiceImpl implements UserService {
private UserDao userDao = new UserDaoImplForOracle(); // 面向具体编程,有具体的实现类
private UserDao userDao;// 面向接口编程
@Override
public void deleteUserById(int userId) {
userDao.deleteUserById(userId);
}
}
4. 面向接口编程思想
可能有人会认为上面的代码已经面向接口编程了:
确实已经面向接口编程了
但对象的创建是:new UserDaoImplForOracle()显然并没有完全面向接口编程,还是使用到了具体的接口实现类。
什么叫做完全面向接口编程?什么叫做完全符合依赖倒置原则呢?
如果代码是这样编写的,才算是完全面向接口编程,才符合依赖倒置原则。
重新执行程序后会发现:
但是这样userDao是null,在执行的时候就会出现空指针异常。
所以我们要解决这个问题,解决空指针异常的问题,其实就是解决两个核心的问题:
- 第一个问题:谁来负责对象的创建。【也就是说谁来:new UserDaoImplForOracle()/new UserDaoImplForMySQL()】
- 第二个问题:谁来负责把创建的对象赋到这个属性上。【也就是说谁来把上面创建的对象赋给userDao属性】
如果我们把以上两个核心问题解决了,就可以做到既符合OCP开闭原则,又符合依赖倒置原则。
那么要怎样做到呢?可以使用“控制反转”这种编程思想来解决这个问题
那么什么是控制反转?
5. 控制反转IoC
控制反转(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计思想,可以用来降低代码之间的耦合度,符合依赖倒置原则。
控制反转的核心是:将对象的创建权交出去,将对象和对象之间关系的管理权交出去,由第三方容器来负责创建与维护。
反转的是两件事:
- 将对象的创建权/管理权交出去了,不再使用硬编码的方式了
- 把对象关系的管理权交出去了,也不再使用硬编码的方式了
控制反转常见的实现方式:依赖注入(Dependency Injection,简称DI)
控制反转是一种思想,依赖注入是这种思想的具体实现。
依赖注入,“依赖”是什么意思?“注入”是什么意思?
- 依赖:A对象和B对象的关系
- 注入:是一种手段,通过这种手段,可以让A对象和B对象产生关系
- 依赖注入:A对象和B对象之间的关系,靠注入的手段来维护,而注入包括set注入和构造器注入
依赖注入DI,又包括两种方式:
-
set方法注入(执行set方法给属性赋值)
-
构造方法注入(执行构造方法给属性赋值)
而Spring框架就是一个实现了IoC思想的框架。
IoC可以认为是一种全新的设计模式,但是理论和时间成熟相对较晚,并没有包含在GoF中。(GoF指的是23种设计模式)
6. Spring框架
Spring框架就是一个实现了IoC思想的框架,在Spring框架中,它可以帮助我们做到:
- 帮我们new对象
- 将new出来的对象赋到属性上
换句话说,Spring框架可以帮助我们创建对象,并且可以帮助我们维护对象和对象之间的关系。比如:
Spring可以new出来UserDaoImplForMySQL对象,也可以new出来UserDaoImplForOracle对象,并且还可以让new出来的dao对象和service对象产生关系(产生关系其实本质上就是给属性赋值)。
7. 总结
Spring框架是什么?
Spring框架就是一个实现了IoC思想的框架,IoC思想就是控制反转思想。
控制反转是什么?用来解决什么问题?
将对象的创建权交出去,将对象和对象之间关系的管理权交出去,由第三方容器来负责创建与维护
解决代码之间的耦合度问题,可以降低代码之间的耦合度,让代码符合OCP开闭原则和依赖倒置原则。
反转是是什么?
反转的是两件事,将对象的创建权/管理权交出去了,把对象关系的管理权交出去了。
怎么实现控制反转?
依赖注入
依赖注入是什么?
依赖就是对象直接的关系,注入是一种手动,通过这种手段让对象之间产生关系。
怎么实现依赖注入?
两种实现方式,set注入和构造器注入,set注入就是执行set方法给属性赋值,构造器注入就是执行构造方法给属性赋值
术语
OCP:开闭原则(开发原则)
DIP:依赖倒置原则(开发原则)
IoC:控制反转(一种思想,一种新型的设计模式)
DI:依赖注入(控制反转思想的具体实现方式)