什么是容器?
相信听到容器很多人都不陌生,大家或多或少都听过容器的名词,那么灵魂三问:容器是做什么的呢?在系统中起到了什么样的作用?怎么去使用容器?
我按照我的理解整理了如下内容,如果有不对的地方欢迎大家指出
首先我们先替换一下概念,我们将容器看成一个集装箱,我不知道你是否理解集装箱是什么,但我还是说明一下。
集装箱:远洋货物运输的单位,集装箱里面装载着各种各样的东西,比如一个分区是专门存放衣服的,一个分区是专门存放电子产品的,一个分区是专门存放食品的,等等。
如果容器类似集装箱,那么容器里面存放的就是一个一个对象。在向容器里面存放对象的时候,容器知道如何去实例化这个对象,并且知道实例化这个对象所需要的外部依赖(依赖注入)
容器是做什么的?
容器就是一个存放对象集合的一个对象。里面存放着不同类的实例。
在系统中起到了什么作用?
不知道怎么解释,但我还是形象的说明一下,比如程序运行的某一个地方需要数据库连接对象,那么我在这个地方创建一个数据库对象连接使用,这样写并没有错,那么程序继续往后面执行,在后面的某一个地方同样需要数据库连接对象,这时候我们再实例化一个吗? 再实例化一个数据库连接对象就造成了一种资源的浪费,因为我们前面已经实例化了一个,我们能否通过某种途径去复用前面生成的数据库连接对象。这时候容器的作用就凸显出来了,我们获取数据库连接对象的途径并不是在使用的位置实例化,而是将这个步骤交给容器去实现,从容器中获取数据库连接对象,当然容器在实例化数据库对象的时候它自己知晓怎么去实例化一个数据库对象(比如实例化数据库对象需要在示例化的时候告知数据库地址、端口、用户名、密码、字符集)。
在程序的使用中我们可以通过获取实例对象去获取需要的对象。
容器在实例化对象的时候,只有第一次访问会实例化该对象,之后再次访问都会复用之前实例化的对象。(资源的复用)
怎么去使用容器?
容器的使用需要根据你的实际情况去设计,比如上面的示例中说了,数据库连接对象就可以放进容器,因为在程序的运行过程中,我不知道在什么地方会使用到数据库连接对象,那么放入容器就行了,需要的时候从容器中去拿,如果整个程序的运行都没有使用到数据库对象,那么也无所谓,因为没有使用所以容器也不会去实例化这个对象。(也没有造成资源浪费)
容器在实际生产中的作用
既然容器是用来管理对象的,那么在此基础之上结合 设计思想-依赖倒置原则 引入了新的设计思想 接口绑定至实现
什么是接口绑定至实现?
接口绑定是指将一个接口或一组接口与相应的实现绑定在一起的过程,就是可以得到接口实现类的一个对象。
接口绑定至实现有什么作用?
我们假设一个场景
以用户中心为例,现提供一个获取用户信息、更新用户信息的接口 UserServiceInterface。
接口实现是 class UserMysqlService 。class UserMysqlService 的数据支持目前都是走mysql 数据库支持。
如果未搭配接口绑定至实现,大家使用UserServiceInterface时时直接 new UserMysqlService ,那么,就会产生耦合点(违背依赖倒置的原则)
之后,突然有一天,用户中心数据进行了数据迁移(由mysql迁移到了oracle),且用户中心对外公布了将放弃对UserMysqlService的维护,将替换新的实现类维护 UserOracleService 。 此时,对用户中心的业务依赖方就痛苦了,需要全局找所有依赖用户中心接口的地方(不合理的架构,命名变动方是用户中心,却要我们所有依赖用户中心的服务都跟着改),并将其改为UserOracleService,且还不清楚改完后是否会产生什么未知的错误。
针对以上问题,接口绑定至实现的作用就凸显出来了
我们在容器中声明 UserServiceInterface::class=>UserMysqlService:class 告知容器,UserServiceInterface的实现类是UserMysqlService。
我们在使用用户中心服务时,直接 ApplicationContext::get( UserServiceInterface::class) 从容器中获取出对应接口的实例来使用(符合依赖倒置原则,合理,不管你如何变,我就从容器里面抽出你的接口实例用就行了,底层接口的实现由你用户中心自己维护)。
这样,不管用户中心如何改变其底层实现,对外部均是无感的(如上例子,用户中心迁移后,容器的配置为 UserServiceInterface::class=>UserOracleService:class 改变其接口实现类即可。)
综上,接口绑定至实现的本质作用就是降低耦合。