开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 3 天,点击查看活动详情
# 高效率学习Spring框架(二)----IOC概念和作用
1、IOC概念
IoC 全称为 Inversion of Control,翻译为 “控制反转”。
【1】控制什么
控制对象的创建和销毁
【2】反转什么
将对象的控制权(创建和销毁)交给IOC容器
2、IOC作用
【1】IOC作用概述
IOC的作用:解耦
我们知道在面向对象设计的软件系统中,它的底层都是由N个对象构成的,各个对象之间通过相互合作,最终实现系统地业务逻辑 ,如图所示:
IOC理论提出的观点:借助于“第三方”实现具有依赖关系的对象之间的解耦。 如图所示:
大家看到了吧,由于引进了中间位置的“第三方”,也就是IOC容器,使得A、B、C、D这4个对象没有了耦合关系,齿轮之间的传动全部依靠“第三方”了,全部对象的控制权全部上缴给“第三方”IOC容器,所以,IOC容器成了整个系统的关键核心,它起到了一种类似“粘合剂”的作用,把系统中的所有对象粘合在一起发挥作用,如果没有这个“粘合剂”,对象与对象之间会彼此失去联系,这就是有人把IOC容器比喻成“粘合剂”的由来。
我们再来做个试验:把上图中间的IOC容器隐藏,然后再来看看这套系统
我们现在看到的画面,就是我们要实现整个系统所需要完成的全部内容。这时候,A、B、C、D这4个对象之间已经没有了耦合关系,彼此毫无联系,这样的话,当你在实现A的时候,根本无须再去考虑B、C和D了,对象之间的依赖关系已经降低到了最低程度。
【2】生活中IOC解耦场景描述
回家场景代码描述
1、创建一个车
2、使用车
3、回收车
【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结构如下
【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、回家、上班、约会多个场景小王都需要依赖车
2、换车之后都要修改CarService的业务代码
【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结构如下:
【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】把创建出来的对象事先都存起来,当我们使用时可以直接从存储容器中获取。
存哪去?
由于我们是很多对象,肯定要找个集合来存。这时候有 Map 和 List 供选择。
到底选 Map 还是 List 就看我们有没有查找需求。有查找需求,选 Map。
所以我们的答案就是在应用加载时,创建一个 Map,用于存放bean对象。
我们把这个 map 称之为容器。
什么是IOC工厂
事先加载bean,并且提供一个直接获取bean的方法。
什么是控制反转
主动new对象方式--被动从容器中获取