• Spring之IOC控制反转


    Spring之IOC控制反转

    0.引言

    今天为一个同学解释什么是控制翻转,但是说来说去都不能完全叙述明白,所有特地写这篇笔记来探究以下究竟什么是控制翻转。。

    1.基本介绍

    首先上一个IOC的官方概念:

    项目的依赖(对象)仅通过构造器参数,工厂方法参数或者通过构造器或工厂方法返回的实例对象的set方法来定义他们的依赖(属性)。容器在bean对象创建时,将依赖注入到bean对象中。这个过程通过使用类的直接构造或服务定位模式机制,从根本上反转了bean本身控制实例化或依赖的位置 。

    2.举个栗子

    对于刚开始看到小伙伴一定是一脸懵逼,甚直对于一些职场所谓的老鸟都不一定能解释这段话到底是什么意思?那我就通过一个例子来开始解释什么是IOC控制反转。

    现在的MVC开发框架,后端开发组成大致是可以分为以下三个部分:

    这种模式特别像饭店的经营模式

    至于说为什么要这样经营,大家可以看看设计模式中的代理模式,我的博客中也有。因为对于一个饭店来说,不仅是提供炒饭烧菜这样基本的功能,更要提供一些其他服务,毕竟很多时候顾客买的是服务,菜好不好吃差不多就行了。

    同样对于上面的开发组成部分,Dao层主要提供数据持久化,访问数据的功能,对于如何增强这些数据,还是要通过服务层来实现,这也是面向切面编程(AOP)的主要概念。

    好了,说了这么多概念,现在要进入实战代码的演示了:

    假设我们的饭店是这样的结构:

    后厨Dao主要功能是做菜,主要菜系或者说套餐有川菜和徽菜

    • cookDao
    public interface CookDao {
        public void cook();
    }
    
    
    • ChuanCaiImpl
    public class ChuanCaiImpl implements CookDao {
        @Override
        public void cook() {
            System.out.println("川菜套餐:水煮鱼,回锅肉,麻婆豆腐 ....");
        }
    }
    
    • HuiCaiImpl
    public class HuiCaiImpl implements CookDao {
        @Override
        public void cook() {
            System.out.println("徽菜套餐:臭鳜鱼,老母鸡汤,虎皮毛豆腐....");
        }
    }
    

    跑堂Service主要功能是传菜(告诉后堂做什么菜,上菜)

    • 上菜接口WaiterService
    public interface WaiterService {
        public void passDish();
    }
    
    • 业务实现类WaiterImpl
    public class WaiterImpl implements WaiterService {
        // 告诉后堂烧什么菜
        private CookDao cookDao = new ChuanCaiImpl() ;
        
        // 上菜
        @Override
        public void passDish() {
            cookDao.cook();
        }
    }
    

    前台主要是让顾客入座,生成顾客订单,顾客要求上菜的时候开始上菜。

    public class HotelController {
        public static void main(String[] args) {
            // 让customer入座
            WaiterService customer = new WaiterImpl();
            // 客户要求上菜
            customer.passDish();
    
        }
    }
    

    输出结果

    川菜套餐:水煮鱼,回锅肉,麻婆豆腐 ....
    

    用一张图可以形象的表示以上的流程:

    从上面可以看出我们并不知到顾客点的是什么菜,通过Service的WaiterImpl我们可以知道顾客点的是川菜ChuanCaiImpl()。可以看出顾客只是提出了点什么菜,而真正主导的还是跑堂,如果说跑堂出现了什么差错,或者说是Service层出现了Bug,这笔订单是无法生成的。

    但是,

    当第二个顾客来了,但他要求吃川菜

    public class HotelController {
        public static void main(String[] args) {
            WaiterService customer = new WaiterImpl();
            // 顾客二入座
            WaiterService customer2 = new WaiterImpl();
            customer.passDish();
            // 顾客二点餐
            customer2.passDish();
    
        }
    }
    

    输出结果

    川菜套餐:水煮鱼,回锅肉,麻婆豆腐 ....
    川菜套餐:水煮鱼,回锅肉,麻婆豆腐 ....
    

    显然,顾客要是想吃川菜还好,但是万一要吃徽菜这样就麻烦了。

    有的同学说,吃徽菜叫跑堂的改菜单订单不久完了吗?改成 private CookDao cookDao = new HuiCaiImpl() ;但是这个时候川菜已经生成订单,跑堂都忙的不可开交,没有时间在进行订单修改了。

    这时候又有同学说那就多创建几个Service跑堂,这样不就成功完成了多个客户的不同需求了吗?但是饭店的老板也想挣钱,节约成本,不压榨完一个跑堂能罢休吗?多请几个跑堂这辈子是不能的。

    老板不仅不请员工,还给跑堂小哥撂下一句狠话:传菜的小事情都干不了,不行就走人!!!!

    以上虽然是在开玩笑,但是对于开发者来说,一个相同的需求尽量要通过同一个功能进行实现,避免重复造轮子,在提高了代码的冗余性同时,加大了维护的难度。例如:假如依了跑堂小哥多请几个人进行不同菜系的分工,然而万一饭店做大了,菜的品类也多了,或者说某个菜系有所更改,那么相应的订单一律都需要更改,这是很麻烦的。相应的,在开发者眼里这样做的代价过于昂贵。

    跑堂小哥表示我只想安安静静的做个传菜的美男子,不行天天记这些菜单和订单,你们给我的压力太大了,一旦出现了bug都要我来负责。

    所以说,对于这套功能实现,完全是程序本身进行控制的。

    然而

    跑堂小哥好日子终于来了,饭店引入了二维码点餐功能。这个功能,用户自己点餐,而且菜单自动传到后堂,这个方法实现的方法如下:

    • HotelController
    public class HotelController {
        public static void main(String[] args) {
            // 顾客1入座
            WaiterImpl customer = new WaiterImpl();
            // 顾客点餐,并指定徽菜
            customer.setCookDao(new HuiCaiImpl());
            // 上菜
            customer.passDish();
            
            // 顾客2入座
            WaiterImpl customer2 = new WaiterImpl();
            // 顾客点餐,并指定川菜
            customer2.setCookDao(new ChuanCaiImpl());
            // 上菜
            customer2.passDish();
    
        }
    }
    
    • WaiterImpl
    public class WaiterImpl implements WaiterService {
        // 告诉后堂烧什么菜
        private CookDao cookDao;
    	
    	// 获得用户的点餐
        public void setCookDao(CookDao cookDao) {
            this.cookDao = cookDao;
        }
    
        // 上菜
        @Override
        public void passDish() {
            cookDao.cook();
        }
    }
    
    

    输出结果:

    徽菜套餐:臭鳜鱼,老母鸡汤,虎皮毛豆腐....
    川菜套餐:水煮鱼,回锅肉,麻婆豆腐 ....
    

    跑堂小哥哭了,原来用简单得set方法就能实现自助点餐系统,而且还直接传到后堂。于是跑堂小哥开开心心的每天专注完成上菜功能。

    从上面的实现过程可以看出,用户是完全主导上菜的种类,中间的服务层完全是通过自动获取对象来通知后堂的。

    总结:控制主导权从程序本身主导,变了用户进行主导,所以说形成了控制反转!!!!!!

    那么Spring为什么说是IOC是项目的依赖(对象)仅通过构造器参数,工厂方法参数或者通过构造器或工厂方法返回的实例对象的set方法来定义他们的依赖(属性)。容器在bean对象创建时,将依赖注入到bean对象中。这个过程通过使用类的直接构造或服务定位模式机制,从根本上反转了bean本身控制实例化或依赖的位置

    什么是容器?

    <beans>
        <bean id="WaiterImpl" class="service.WaiterImpl">
            <property name="cookDao" ref="ChuanCaiImpl"></property>
        </bean>
    </beans>
    

    这就是容器?什么作用?就是上面的WaiterImpl,id是类名,class是全路径名,ref相当于set方法。如何创建对象,当然是getbean方法,所以说IOC最后一句话是:从根本上反转了bean本身控制实例化或依赖的位置。。。

    那么注解的是啥是不是就迎刃而解了。

  • 相关阅读:
    软件项目开发典型风险一览
    删除数据库所有表数据
    今天愚人节,教大家一个真正的最强整人方法
    潘正磊谈微软研发团队管理之道(下)
    追MM与23种设计模式
    22个所见即所得在线 Web 编辑器
    神奇的js代码,图片全都飞起来了
    字体 小 中 大
    使用ODP.NET连接Oracle数据库一个OracleCommand运行多条SQL语句的方法
    删除SQL数据库中所有的表
  • 原文地址:https://www.cnblogs.com/wigginess/p/13961656.html
Copyright © 2020-2023  润新知