Spring6 -(01)案例引出 Spring 概念

image-20230206074352715

Spring6 -(01)案例引出 Spring 概念

Srpring6支持的JDK版本为17

1. 环境准备

1.1 创建工程

image-20230318085412009

1.2 配置JDK版本

File -> Project Structure

image-20230318090240880

1.3 配置Maven环境

File -> Settings -> Build,Execution,Deployment -> Build Tools -> Maven

image-20230318085944584

1.4 创建一个模块

右键工程spring6 -> New -> Module

image-20230318090609094

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);
    }
}

执行结果:

image-20230318094723316

可以看出,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依赖倒置原则

上面例子的依赖关系:

image-20230318100704348

上层是依赖下层的。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. 面向接口编程思想

可能有人会认为上面的代码已经面向接口编程了:

image-20230318102631694

确实已经面向接口编程了

但对象的创建是:new UserDaoImplForOracle()显然并没有完全面向接口编程,还是使用到了具体的接口实现类。

什么叫做完全面向接口编程?什么叫做完全符合依赖倒置原则呢?

image-20230318102800404

如果代码是这样编写的,才算是完全面向接口编程,才符合依赖倒置原则。

重新执行程序后会发现:

在这里插入图片描述

但是这样userDao是null,在执行的时候就会出现空指针异常。

所以我们要解决这个问题,解决空指针异常的问题,其实就是解决两个核心的问题:

  • 第一个问题:谁来负责对象的创建。【也就是说谁来:new UserDaoImplForOracle()/new UserDaoImplForMySQL()】
  • 第二个问题:谁来负责把创建的对象赋到这个属性上。【也就是说谁来把上面创建的对象赋给userDao属性】

如果我们把以上两个核心问题解决了,就可以做到既符合OCP开闭原则,又符合依赖倒置原则。

那么要怎样做到呢?可以使用“控制反转”这种编程思想来解决这个问题

那么什么是控制反转?

5. 控制反转IoC

控制反转(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计思想,可以用来降低代码之间的耦合度,符合依赖倒置原则。

控制反转的核心是:将对象的创建权交出去,将对象和对象之间关系的管理权交出去,由第三方容器来负责创建与维护

反转的是两件事:

  • 将对象的创建权/管理权交出去了,不再使用硬编码的方式了
  • 把对象关系的管理权交出去了,也不再使用硬编码的方式了

控制反转常见的实现方式:依赖注入(Dependency Injection,简称DI)

控制反转是一种思想,依赖注入是这种思想的具体实现

依赖注入,“依赖”是什么意思?“注入”是什么意思?

  • 依赖:A对象和B对象的关系
  • 注入:是一种手段,通过这种手段,可以让A对象和B对象产生关系
  • 依赖注入:A对象和B对象之间的关系,靠注入的手段来维护,而注入包括set注入和构造器注入

image-20230318110036268

依赖注入DI,又包括两种方式:

  • set方法注入(执行set方法给属性赋值)

  • 构造方法注入(执行构造方法给属性赋值)

Spring框架就是一个实现了IoC思想的框架。

IoC可以认为是一种全新的设计模式,但是理论和时间成熟相对较晚,并没有包含在GoF中。(GoF指的是23种设计模式)

6. Spring框架

Spring框架就是一个实现了IoC思想的框架,在Spring框架中,它可以帮助我们做到:

  • 帮我们new对象
  • 将new出来的对象赋到属性上

换句话说,Spring框架可以帮助我们创建对象,并且可以帮助我们维护对象和对象之间的关系。比如:

image-20230318103302160

Spring可以new出来UserDaoImplForMySQL对象,也可以new出来UserDaoImplForOracle对象,并且还可以让new出来的dao对象和service对象产生关系(产生关系其实本质上就是给属性赋值)。

7. 总结

Spring框架是什么?

​ Spring框架就是一个实现了IoC思想的框架,IoC思想就是控制反转思想。

控制反转是什么?用来解决什么问题

​ 将对象的创建权交出去,将对象和对象之间关系的管理权交出去,由第三方容器来负责创建与维护

​ 解决代码之间的耦合度问题,可以降低代码之间的耦合度,让代码符合OCP开闭原则和依赖倒置原则。

反转是是什么?

​ 反转的是两件事,将对象的创建权/管理权交出去了,把对象关系的管理权交出去了。

怎么实现控制反转?

​ 依赖注入

依赖注入是什么?

​ 依赖就是对象直接的关系,注入是一种手动,通过这种手段让对象之间产生关系。

怎么实现依赖注入?

​ 两种实现方式,set注入和构造器注入,set注入就是执行set方法给属性赋值,构造器注入就是执行构造方法给属性赋值

术语

​ OCP:开闭原则(开发原则)

​ DIP:依赖倒置原则(开发原则)

​ IoC:控制反转(一种思想,一种新型的设计模式)

​ DI:依赖注入(控制反转思想的具体实现方式)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值