zoukankan      html  css  js  c++  java
  • 简明依赖注入(Dependency Injection)

    前言

    这是因特奈特上面不知道第几万篇讲依赖注入(Dependency Injection)的文章,但是说明白的却寥寥无几,这篇文章尝试控制字数同时不做大多数。

    首先,依赖注入的是一件很简单的事情。

    为什么需要依赖注入

    然后,假设我们有一个汽车Car,一个引擎接口Engine,两个引擎具体实现Level4Engine,Level5Engine。汽车可以长这样:

    public class Car{
    	private Engine e;
    	public Car(){
    		e = new Level4Engine();
    	}
    	public void ignite(){
    		System.out.println()
    	}
    }
    

    现在要让汽车点火,简单:

    public static void main(String[] args) {
        Car c = new Car();
        c.ignite();
    }
    

    但是假如我们想要换一个更高级的引擎,我们不得不修改Car的构造函数:
    ~~ e = new Level4Engine(); ~~
    e = new Level5Engine();
    然后重新编译。这就是代码的耦合,一方面假如需求不会经常改变,这个汽车只会使用Level4Engine,那没问题,这个代码很完美。但另一方面,假如引擎有多个,需求会经常改变,我们发现Level4Engine还不行,需要更高级的,而且新引擎还需要进行一系列复杂配置,那这个耦合就是灾难了。只是装配汽车的血汗工人,懂不了那么多的。

    怎么进行依赖注入

    依赖注入就是为了解决上述问题而生的。用依赖注入的写法解决上面的问题:

    public class Car{
    	private Engine e;
    	public Car(Engine e){
    		this.e = e;
    	}
    	public void ignite(){
    		System.out.println()
    	}
    }
    
    // 也可以使用xml进行配置
    @Confignuration
    public CarFactory{
        @Bean
        public Engine engine(){
            var e = new Level5Engine();
            e.complexConfig();
            return e;
        }
        @Bean
        public Car car(Engine e){
        	return new Car(e);
        }
    }
    

    这里Car对Engine的依赖被抽了出去。Car不负责创建Engine,也不负责/无能力配置Enging。那么Engine抽出到了哪?又由谁注入给Car?总不能让Car对着一个壳子(Engine接口)点火吧。

    答案当然是spring。spring把它们抽象为Bean,每个@Bean都通知spring
    嘿我要给你一个新的bean,以后就交给你来管理了。

    DI的优势

    这样既解决了上述"汽车装配工需要引擎配置知识"的问题,也解决了"更改引擎非常困难"的问题:

    • 引擎制造者只关注如何制造出引擎,当现在生产条件不成熟就提供Level4Engine,反之就提供Level5Engine,可以随时更改并对其进行配置
    • 汽车装配工只关注装配工作,而不需要配置引擎。
    • 每次引擎更改后只需要对这个配置类进行编译,如果使用xml连编译也不需要了。

    这真的就是依赖注入的全部内容了,不过围绕依赖注入相关还有很多话题可以讨论,下面扩展就是两个。

    扩展1:使用自动装配代替手动装配

    演示了在CarFactory中手动car,还没完,spring还能更聪明一些,它可以通过自动装配完成这个配置工作:

    @Component
    public class Car{
    	private Engine e;
    
    	@Autowired
    	public Car(Engine e){
    		this.e = e;
    	}
    
    	public void ignite(){
    		System.out.println()
    	}
    }
    
    @Component
    public class Level5Engine{
    	public void complexConfig(){
    		System.out.println("really complex stuff...");
    	}
    }
    
    @Confignuration
    @ComponentScan
    public class CarFactory{}
    

    CarFactory@ComponentScan告诉spring扫描当前类所在包下面的所有类,如果找到@Component注解就加入spring bean容器。这里明显Car和Level5Engine加入了容器(默认会类名首字母小写,所以加入的是carlevel5Engine)。然后@Autowired在当前容器中查找,如果找到需要注入的类型就自动注入:

    	@Autowired
    	public Car(Engine e){
    		this.e = e;
    	}
    

    Car的装配需要一个引擎,spring容器刚好有一个实现了Engine的Level5Engine引擎,所以这里自动注入。

    扩展2: NoUniqueBeanDefinitionException自动装配歧义

    最后一个不常见的问题,假如我们把两个引擎都标注了@Component会怎么样:

    @Component
    public class Level5Engine{
    }
    @Component
    public class Level4Engine{
    }
    

    spring不知道用哪一个注入给car,所以抛出NoUniqueBeanDefinitionException,表示有多个候选注入对象,需要我们手动缩小范围(@Qualifier,@Component value,@Primary),关于这部分内容可以参见其他文章。

  • 相关阅读:
    nginx.conf中配置laravel框架站点
    centos6.4下安装php7+nginx+mariadb环境
    Windows Terminal 安装和运行
    微软 WSL 重装操作系统
    Pulumi 如何在 Windows 环境中设置
    Ubuntu 20.04 安装 JDK
    代码的 Lint 是什么意思
    CentOS 8 手动安装 Go 1.16 版本
    Raspberry Pi 安装 go 后提示错误 Exec format error
    系统管理--查看网卡、内存等
  • 原文地址:https://www.cnblogs.com/ysherlock/p/10014031.html
Copyright © 2011-2022 走看看