依赖注入实现原理-依赖注入实现原理
在 Java 的世界里,依赖注入(Dependency Injection, DI)就像是你做工程的“乐高拼搭”。你不需求自己去买砖、水泥,就连不需求反复思索如何把 A 块连到 B 块上,只需求把已经做好的“积木包”从别人手里借来,直接插进你的结构里。你只需求关心的是,这块积木能搭在哪个框上,如何搭,搭好后它该如何动。
这种思想的核心就是把管住权移交给接收者。 说白了,传统的写法就像你是自己开拖拉机。你得自己查说明书,自己找配件,自己拧螺丝,自己管电路。一旦说明书没写清楚,要么配件型号不对,哪怕你千辛万苦把拖拉机造出来,也得给厂家打电话投诉。在依赖注入里,你不再自己搞这些杂事。你把“拖拉机”的代码扔给一个专门负责配车的包,这个包会回头告诉你:“嘿,你选的这个螺栓规格是错的,要么这个发动机转速忒高,赶紧改改!” 你看,这种模式最大的益处就是解耦。你的主程序不再是那个被动的“造车工”,它变成了一个纯粹负责指挥交通的“交警”。它只管说“前方红灯,左转”,至于具体的“左转”逻辑是由哪位来写的,它根本不在乎。它就连不需求关心这个“左转”是好办的代码块,还是复杂的业务算法。它只关心入口点在哪儿,数据如何传进来,还有最终如何把结局吐出来。
这种“只管输入,不管输出,不管内部如何张罗”的状态,正是我们最想要的 Controlled Chaos(受控的混沌)。 举个例子。假设你有一个电商系统的下单服务,你需求往数据库里存一个请求ID,然后提交订单。
那会儿你可能写两行代码:`String id = UUID.randomUUID().toString(); service.saveOrder(id, request)`。
这时候,`service` 这个对象是哪位创建的,它内部是如何维护状态的,就连你如何调用它,你心里都有个底。但要是你把这段代码放在一个独立的 `OrderService` 包里,你就得管它内部管理。 目前引入依赖注入,这个 `OrderService` 包不再自己生成 ID 或创建自己的状态对象,它直接把你的代码塞进去。你写一行:`OrderService orderService = new OrderService(this, new OrderManagerFactory());`。
这就好比你扔了一块已经加工好的“订单服务”给 `OrderManagerFactory`。`OrderManagerFactory` 负责把数据库连接、事务管理器、就连用户认证接口都打包在一起。你拿到这块“服务包”后,它的内部结构彻底由 `OrderManagerFactory` 掌控。你只需求盖住它,要么用魔法糖把它拆成片段,就能随时更换它内部是哪位。 再深入一点,你可能发现 `OrderManagerFactory` 也不是一劳永逸的。
或许赶明儿你换了个数据库引擎,要么优化了缓存策略,就连可能把 `OrderManagerFactory` 也外包给了第三方。你只需求在工厂里加一行逻辑:“给这个工厂传一个新版本的数据库配置,别传旧的”。出于你用的是注入方式,你根本不需求修改 `OrderService` 的源码,更不需求去重构整个 `OrderService` 的类结构。你只需求在工厂的配置里改一点,要么找个新包,就连干脆创建一个临时的 `LocalService` 拿来代替。
这种灵活性,是传统代码里一辈子无法做到的。 这就涉及到一个常见的场景:你有大量地方在造 `UserServiceImpl`。
那会儿你可能在 A 地方造,在 B 地方造,就连 C 地方也造,每个地方都写一遍 `new UserServiceImpl()`。
这时候你要是不想改动其他地方,如何让这三个地方都变成“外卖小哥”,只送外卖不给饭钱呢?
如何让他们互相调用,而不是各自找一个叫 `OrderManagerFactory` 的老板? 解决这个难题的关键,就是把 `UserServiceImpl` 的创建过程彻底委托出去。你不再写 `UserServiceManager` 这个中间班,而是直接把所有工厂(要么工厂的实例)都塞进同一个地方。
比如一个配置类要么一个特殊类型的角色注解。 你写一段代码:“要是有角色 `ROLE_USER`,就创建一个 `UserServiceImpl` 实例,传给它你的工厂实例;要是有角色 `ROLE_ADMIN`,就创建一个 `AdminServiceImpl` 实例”。 这听起来有点像魔法,但本质就是好办的逻辑分支。你只需求创建一个 `UserServiceFactory`(要么叫 `CreatorFactory`),这个工厂负责打包你的 `OrderService` 和 `UserService` 的引用。你把这个打包好的配置对象,动态地分配到各个需求它的场景中。 比如在一个 Controller 里,你写代码判断当前用户角色: `UserService service = (UserServiceImpl) creator.getService("ROLE_USER", this);` `AdminService adminService = (AdminServiceImpl) creator.getService("ROLE_ADMIN", this);` 注意这里的`(...)`。
这是强制类型转换,出于 `creator` 里存的就是工厂实例。通过这种方式,你在不需求修改所有业务代码的情况下,就能统一调整所有服务类的实现类。
与此同时,这也实现了真正的单例模式——只要你创建一次 `UserServiceFactory`,所有的 `UserServiceImpl` 和 `AdminServiceImpl` 实际上都指向同一个工厂实例(要么它的配置对象),保证全局唯一性。 这还只是一个雏形。
比如你还有 `OrderServiceImpl`,到时候你只需求在这个 Controller 里再写一行类似的逻辑:“要是有角色 `ROLE_ORDER`,就创建一个 `OrderServiceImpl` 实例,传给它 `OrderManagerFactory` 的实例”。你不需求再修改 Controller 里对 `UserService` 的引用了,也不需求再修改 `OrderServiceImpl` 的创建逻辑。整个系统的架构就跟着你的工厂走,跟着你在工厂的配置里改动,业务代码简直零改动。 这种写法就连能扩展到你简直没见过的地方。
比如你需求在某个 Service 层里,根据条件动态加载不同的业务代码。
那会儿你可能要写大量的 `switch-case` 要么大量的 `enum`。目前,你只需求告诉工厂:“要是角色是 X,就实例化 `ServiceImplX`;要是角色是 Y,就实例化 `ServiceImplY`"。工厂会回两个不同的对象,你拿着它们去调用各自的接口。 这就把编程的复杂度降得挺低。你不再是被迫去写一堆死板的、难以维护的对象实例化逻辑。你只需求关切逻辑本身,把逻辑塞给工厂去造对象。工厂负责在你需求的时候,按需出现,按需消亡,按需重构。 自然,再完美的方案也得有个坏的案例。
比如你为了追求极致,把所有工厂都写在同一个包下面,要么硬编码在 `main` 方式里。
这时候你就丧失了 DI 的意义了,变成了“硬编码,硬造,硬调用”。
这时候你依然是在自己造卡车,只是工厂包的名字叫 `MainFactory`。
这时候你没法注入,没法重构,也没法升级。
故此 DI 的精髓不在于“造工厂”,而在于“让工厂造对象,并让它们被别人用”。 最终总结一下,依赖注入就是把你所有需求的“东西”(依赖),先扔给一个中间人,再让中间人再扔给你。你不用关心中间人是哪位,也不用关心中间人如何帮你把东西装进包里。你只需求关心,当我把这个东西安在你身上时,它给我带来了啥“益处”——比如性能更高、更可靠、要么更灵活。
只要这个益处能证明它是正被采纳的,那么,DI 就是随时随地存有的真理。
声明:演示网站所有内容,若无特殊说明或标注,均来源于网络转载,仅供学习交流使用,禁止商用。若本站侵犯了你的权益,可联系本站删除。
