高效率学习Spring框架(二)----IOC概念和作用

183 阅读8分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 3 天,点击查看活动详情

# 高效率学习Spring框架(二)----IOC概念和作用

1、IOC概念

IoC 全称为 Inversion of Control,翻译为 “控制反转”。

【1】控制什么

控制对象的创建和销毁

【2】反转什么

将对象的控制权(创建和销毁)交给IOC容器

2、IOC作用

【1】IOC作用概述

IOC的作用:解耦

我们知道在面向对象设计的软件系统中,它的底层都是由N个对象构成的,各个对象之间通过相互合作,最终实现系统地业务逻辑 ,如图所示:

image-20191115102404343.png

IOC理论提出的观点:借助于“第三方”实现具有依赖关系的对象之间的解耦。 如图所示:

image-20191115104111240.png

大家看到了吧,由于引进了中间位置的“第三方”,也就是IOC容器,使得A、B、C、D这4个对象没有了耦合关系,齿轮之间的传动全部依靠“第三方”了,全部对象的控制权全部上缴给“第三方”IOC容器,所以,IOC容器成了整个系统的关键核心,它起到了一种类似“粘合剂”的作用,把系统中的所有对象粘合在一起发挥作用,如果没有这个“粘合剂”,对象与对象之间会彼此失去联系,这就是有人把IOC容器比喻成“粘合剂”的由来。

我们再来做个试验:把上图中间的IOC容器隐藏,然后再来看看这套系统

image-20191115104422970.png

我们现在看到的画面,就是我们要实现整个系统所需要完成的全部内容。这时候,A、B、C、D这4个对象之间已经没有了耦合关系,彼此毫无联系,这样的话,当你在实现A的时候,根本无须再去考虑B、C和D了,对象之间的依赖关系已经降低到了最低程度。

【2】生活中IOC解耦场景描述

回家场景代码描述

1、创建一个车

2、使用车

3、回收车

image-20191113112803835.png

【3】场景代码(1)

【2.1】目标
1、用代码描述用车场景
2、分析依赖的问题
【3.2】实现
#步骤:
    【1】创建pojo、service.controller层
    【2】创建基础类:Audi;CarService;XiaoWang
    【3】使用CarService调用Audi
【3.2.1】创建项目

新建spring-day01-01car结构如下

01.png

image-20191114101040682.png

image-20191114101141135.png

【3.2.2】pojo层
package com.lucky.spring.pojo;
​
/**
 * @Description:奥迪车
 */
public class Audi {
​
    /**
     * @Description 启动
     */
    public void start(){
        System.out.println("奥迪启动。。。。");
    }
​
    /**
     * @Description 运行
     */
    public void run(){
        System.out.println("奥迪运行。。。。");
    }
​
    /**
     * @Description 停止
     */
    public void stop(){
        System.out.println("奥迪熄火。。。。");
    }
}
​
package com.lucky.spring.pojo;
​
/**
 * @Description:宝马车
 */
public class Bmw {
​
​
    public void start(){
        System.out.println("宝马启动");
    }
​
    public void run(){
        System.out.println("驾驶宝马");
    }
​
    public void stop(){
        System.out.println("宝马熄火");
    }
}
【3.2.3】service层
package com.lucky.spring.service;
​
import com.lucky.spring.pojo.Audi;
​
/**
 * @Description:车辆使用场景
 */
public class CarService {
​
    /**
     * @Description 回家
     */
    public void gotoHome(){
        Audi audi = new Audi();
        audi.start();
        audi.run();
        audi.start();
        System.out.println("到家了。。。");
    }
​
    /**
     * @Description 上班
     */
    public void gotoWork(){
        Audi audi = new Audi();
        audi.start();
        audi.run();
        audi.start();
        System.out.println("到家了。。。");
    }
​
    /**
     * @Description 约会
     */
    public void gotoFindmm(){
        Audi audi = new Audi();
        audi.start();
        audi.run();
        audi.start();
        System.out.println("到约会地点。。。");
    }
}
​
​
【3.2.4】controller层
package com.lucky.spring.controller;
​
import com.lucky.spring.service.CarService;
import org.junit.jupiter.api.Test;
​
/**
 * @Description:小王的生活场景
 */
public class XiaoWang {
​
    @Test
    public void needGotoHome(){
        CarService carService = new CarService();
        carService.gotoHome();
    }
​
    @Test
    public void needGotoWork(){
        CarService carService = new CarService();
        carService.gotoWork();
    }
​
    @Test
    public void needGotoFindMM(){
        CarService carService = new CarService();
        carService.gotoFindMM();
    }
}
​
【3.3】场景小结
1、回家、上班、约会多个场景小王都需要依赖车

image-20191113142228605.png

02.png

2、换车之后都要修改CarService的业务代码

image-20191113114331825.png

03.png

【4】方案一:interface

【4.1】思考
1、咱们想一下,小王是需要一辆宝马?需要一辆奥迪?其实小王只是需要一辆车?
    同学们之前我们是不是学习过接口的概念,这里我们能不能抽象出一个Car的接口呢?
【4.2】目标
1、使用接口与实现分离的方式解耦
【4.3】实现
#步骤:
    【1】添加Car的接口
    【2】Audi、Bmw实现Car接口
    【3】修改CarService
【4.3.1】添加Car接口
package com.lucky.spring.pojo;
​
/**
 * @Description:车接口
 */
public interface Car {
​
    public void start();
​
    public void run();
​
    public void stop();
}
​
【4.3.2】实现Car接口
package com.lucky.spring.pojo;
​
/**
 * @Description:奥迪
 */
public class Audi implements Car{
​
    public void start(){
        System.out.println("奥迪启动");
    }
​
    public void run(){
        System.out.println("驾驶奥迪");
    }
​
    public void stop(){
        System.out.println("奥迪熄火");
    }
}
​
package com.lucky.spring.pojo;
​
/**
 * @Description:宝马
 */
public class Bmw implements Car {
​
    public void start(){
        System.out.println("宝马启动");
    }
​
    public void run(){
        System.out.println("驾驶宝马");
    }
​
    public void stop(){
        System.out.println("宝马熄火");
    }
}
​
【4.3.3】修改CarService
package com.lucky.spring.service;
​
import com.lucky.spring.pojo.Audi;
import com.lucky.spring.pojo.Car;
​
/**
 * @Description:车辆使用场景
 */
public class CarService {
​
    Car audi = new Audi();
​
    /**
     * @Description 回家
     */
    public void gotoHome(){
//        Audi audi = new Audi();
        audi.start();
        audi.run();
        audi.start();
        System.out.println("到家了。。。");
    }
​
    /**
     * @Description 上班
     */
    public void gotoWork(){
//        Audi audi = new Audi();
        audi.start();
        audi.run();
        audi.start();
        System.out.println("到家了。。。");
    }
​
    /**
     * @Description 约会
     */
    public void gotoFindmm(){
//        Audi audi = new Audi();
        audi.start();
        audi.run();
        audi.start();
        System.out.println("到约会地点。。。");
    }
}
​
【4.4】方案一小结
1、局部变量提到成员变量解耦
2、接口与实现分离解耦
3、Car bmw = new Bmw();的时候还是需要指定实现类,由此可以看出,没有本质上解决强依赖的问题
#【话外音】
    #难道上班、约会、回家之前小王自己造个车吗?

【5】方案二:CarFactroy(2)

【5.1】思考
现实生活中,我们不可能在上班、回家、约会之前自己去造车子,而是我们在使用车子之前,去工厂买一辆,车的制造是由汽车工厂完成,我们只需要告诉工厂咱们需要什么牌子的车就好
【5.2】目标
1、用代码模拟汽车工厂
2、理解IOC控制反转的概念
【5.3】实现
#步骤
    【1】增加汽车工厂
        工厂提供车的方式:可以直接调用的
        生产车过程:知道类全路径,使用反射机制实例化类的对象
    【2】修改CarService,使用工厂
【5.3.1】创建项目

拷贝项目spring-day01-01car建立spring-day01-02car-lv1结构如下:

04.png

【5.3.2】添加factory层
package com.lucky.spring.factory;
​
import com.lucky.spring.pojo.Car;
​
/**
 * @Description:汽车工厂
 */
public class CarFactroy {
​
    /**
     * @Description 根据class对象实例化车
     * @param carClass 车辆class对象
     * @return
     */
    public static Car getCar(Class<?> carClass){
        String carClassName = carClass.getName();
        Car car =null;
        try {
            car = (Car) Class.forName(carClassName).newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return car;
    }
​
    /**
     * @Description 根据class对象实例化车
     * @param carClassName 车辆class对象全限定名称
     * @return
     */
    public static Car getCar(String carClassName ){
        Car car =null;
        try {
            car = (Car) Class.forName(carClassName).newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return car;
    }
}
​
​
【5.3.3】修改CarService
package com.lucky.spring.service;
​
import com.lucky.spring.factory.CarFactroy;
import com.lucky.spring.pojo.Audi;
import com.lucky.spring.pojo.Car;
​
/**
 * @Description:车辆使用场景
 */
public class CarService {
​
    Car audi = CarFactroy.getCar(Audi.class);
​
    /**
     * @Description 回家
     */
    public void gotoHome(){
//        Audi audi = new Audi();
        audi.start();
        audi.run();
        audi.start();
        System.out.println("到家了。。。");
    }
​
    /**
     * @Description 上班
     */
    public void gotoWork(){
//        Audi audi = new Audi();
        audi.start();
        audi.run();
        audi.start();
        System.out.println("到公司。。。");
    }
​
    /**
     * @Description 约会
     */
    public void gotoFindmm(){
//        Audi audi = new Audi();
        audi.start();
        audi.run();
        audi.start();
        System.out.println("到约会地点。。。");
    }
}
​
【5.3.4】方案二小结
1、程序中汽车工厂生产车的方式:反射实例化汽车对象
2、程序中汽车工厂--IOC容器

【6】IOC作用小结

IOC-控制反转:
    控制什么:控制对象的创建和销毁;
    反转什么:new-->factory创建;
IOC的作用:
    解耦
IOC是什么:
    工厂: 反射机制实例化对象

3、手写IOC实现【理解】

咱们完成了一个生活中的场景解耦描述,知道了:

  • IOC就是一个工厂

【1】思考

1、手写IOC工厂采用什么方式实例化对象?
    方案:反射机制实例化类的对象
​
2、能不能把实例化的对象,事先加载到集合中?选取什么数据结构的集合合适?
    方案:对象名和对象全限定名是键值对结构,又需要便于存储与查找,使用map集合更适合!
    
3、类似“com.lucky.spring.pojo.Audi”的配置信息怎么处理?
    方案:放到properties文件

【2】目标

1、手写IOC
    反射机制实例化对象
    存储到集合容器
    使用properties文件存储配置

【3】实现(3)

1、创建项目及J2EE三层架构
2、创建pojo对象
3、创建dao对象
4、创建service对象
5、创建controller对象
6、创建properties文件 
7、factory工厂类【重点】
    【1】事先创建集合容器
    【2】事先加载properties文件内容
    【3】反射机制实例化bean对象,并且放入集合容器
    【4】公共的访问方法

"accountDao",beanObject

【3.1】创建项目

新建项目结构如下

package com.lucky.spring.pojo;
​
/**
 * @Description:账户实体类
 */
public class Account {
​
    //账户编号
    private String Id;
​
    //账户所有者
    private String accountName;
​
    //账户余额
    private Float money;
​
    public Account() {
    }
​
    public String getId() {
        return Id;
    }
​
    public void setId(String id) {
        Id = id;
    }
​
    public String getAccountName() {
        return accountName;
    }
​
    public void setAccountName(String accountName) {
        this.accountName = accountName;
    }
​
    public Float getMoney() {
        return money;
    }
​
    public void setMoney(Float money) {
        this.money = money;
    }
​
    @Override
    public String toString() {
        return "Account{" +
                "Id='" + Id + ''' +
                ", accountName='" + accountName + ''' +
                ", money=" + money +
                '}';
    }
}
​
【3.2】dao层
package com.lucky.spring.dao;
​
/**
 * @Description:账户dao层
 */
public interface AccountDao {
​
    /**
     * @Description 新增
     */
    void saveAccount();
​
    /**
     * @Description 删除
     */
    void delAccount();
​
    /**
     * @Description 修改
     */
    void updateAccout();
​
    /**
     * @Description 查询
     */
    void findAccount();
​
​
}
​
​
​
package com.lucky.spring.dao.impl;
​
import com.lucky.spring.dao.AccountDao;
import com.lucky.spring.pojo.Account;
​
/**
 * @Description:
 */
public class AccountDaoImpl implements AccountDao {
​
    private static  Account account;
​
    static {
        account = new Account();
        account.setId("010101");
        account.setAccountName("张三");
        account.setMoney(2000F);
    }
    @Override
    public void saveAccount() {
        System.out.println("保存:"+account.toString());
    }
​
    @Override
    public void delAccount() {
        System.out.println("删除:"+account.toString());
    }
​
    @Override
    public void updateAccout() {
        System.out.println("修改:"+account.toString());
    }
​
    @Override
    public void findAccount() {
        System.out.println("查询:"+account.toString());
    }
}
​
​
​
【3.3】servic层
package com.lucky.spring.service;
​
import com.lucky.spring.dao.AccountDao;
​
/**
 * @Description:用户业务层接口
 */
public interface AccountService {
​
    /**
     * @Description 新增
     */
    void saveAccount();
​
    /**
     * @Description 删除
     */
    void delAccount();
​
    /**
     * @Description 修改
     */
    void updateAccout();
​
    /**
     * @Description 查询
     */
    void findAccount();
​
    void setAccountDao(AccountDao accountDao);
}
​
​
​
package com.lucky.spring.service.impl;
​
import com.lucky.spring.dao.AccountDao;
import com.lucky.spring.service.AccountService;
​
/**
 * @Description:用户业务层接口实现
 */
public class AccountServiceImpl implements AccountService {
​
    private AccountDao accountDao;
​
    @Override
    public void saveAccount() {
        accountDao.saveAccount();
    }
​
    @Override
    public void delAccount() {
        accountDao.delAccount();
    }
​
    @Override
    public void updateAccout() {
        accountDao.updateAccout();
    }
​
    @Override
    public void findAccount() {
        accountDao.findAccount();
    }
​
    @Override
    public void setAccountDao(AccountDao accountDao) {
        this.accountDao = accountDao;
    }
}
​
​
​
【3.4】factory层

在resources目录中建立

db.properties

accountDao = com.lucky.spring.dao.impl.AccountDaoImpl
accountService = com.lucky.spring.service.impl.AccountServiceImpl
package com.lucky.spring.factory;
​
import javafx.beans.binding.ObjectExpression;
​
import java.io.IOException;
import java.util.*;
​
/**
 * @Description:bean工厂
 */
public class BeanFactory {
​
    //1、事先存储容器
    private static Map<String, Object> map = new HashMap<>();
    //2、加载配置文件
    static {
        Properties properties = new Properties();
        try {
            properties.load(BeanFactory.class.getClassLoader().getResourceAsStream("db.properties"));
            Enumeration<?> enumeration = properties.propertyNames();
            while (enumeration.hasMoreElements()) {
                String key = (String) enumeration.nextElement();
                String value = (String) properties.get(key);
                //3、实例化bean
                Object beanObject = Class.forName(value).newInstance();
                //4、放入容器
                map.put(key,beanObject);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
​
​
    //5、公共获得bean
    public static Object getBean(String calssName){
       return map.get(calssName);
    }
}
​
​
【3.5】controller层
package com.lucky.spring.controller;
​
import com.lucky.spring.dao.AccountDao;
import com.lucky.spring.factory.BeanFactory;
import com.lucky.spring.service.AccountService;
import com.lucky.spring.service.Impl.AccountServiceImpl;
import org.junit.Test;
​
/**
 * @Description:
 */
public class ClientController {
    
​
   @Test
    public void saveAccount() {
       AccountService accountService = (AccountService) BeanFactory.getBean("accountService");
       accountService.setAccountDao((AccountDao) BeanFactory.getBean("accountDao"));
       accountService.saveAccount();
    }
​
}
​
​

【4】手写IOC小结

我们通过使用工厂模式,实现了表现层——业务层、业务层——持久层的解耦。 
    【实现思路】:
        工厂+反射+配置文件
    【核心思想】:
        【1】读取配置文件中类的全限定名通过反射机制创建对象。  
        【2】把创建出来的对象事先都存起来,当我们使用时可以直接从存储容器中获取。 
            存哪去?   
                由于我们是很多对象,肯定要找个集合来存。这时候有 MapList 供选择。      
                到底选 Map 还是 List 就看我们有没有查找需求。有查找需求,选 Map。   
                所以我们的答案就是在应用加载时,创建一个 Map,用于存放bean对象。
                我们把这个 map 称之为容器。  
            什么是IOC工厂  
                事先加载bean,并且提供一个直接获取bean的方法。
            什么是控制反转    
                主动new对象方式--被动从容器中获取