依赖注入DI -- Guice
视频教程:https://www.bilibili.com/video/BV1qr4y137qW
理论
- 依赖注入
- 是一种消除类之间依赖关系的设计模式。例如,A 类要依赖 B 类,A 类不再直接创建 B 类,而把这种依赖关系由 Guice (用 abstractModule)或 Spring 容器(xml, bean)根据配置信息来创建、管理
- 为什么需要使用依赖注入
- 比如我们有一个 car class,它有各种对象,比如车轮
- car class 需要创建所有依赖的对象,但我们想要换个品牌的车轮
- 使用依赖注入,不需要创建,只要使用即可
- 依赖注入是代码的中间人,他创建车轮对象,并提供能 car class 使用
- 控制反转 ⇒ 依赖注入背后的概念
- 指一个类不应静态配置其依赖项,而应由其他一些类从外部进行配置
- 这是 S.O.L.I.D 的第五项原则:类应该依赖于抽象,而不是依赖于具体的东西
- 根据这些原则,一个类应该专注于履行其职责,而不是创建履行这些职责的的对象。这就是依赖注入发挥作用的地方,它为类提供了必需的对象。
- 指一个类不应静态配置其依赖项,而应由其他一些类从外部进行配置
- 优势
- 帮助单测
- 依赖关系的初始化由注入器完成,减少了代码
- 扩展程序更加容易
- 松耦合
- 劣势
- 学习有点复杂,过度使用会导致管理和其它问题
- 编译时的错误被推送到运行时
- 影响 IDE 自动化
安装
Guice 在 5.x 版本后整合了低版本的扩展类库,目前使用其所有功能只需要引入一个依赖
<dependency>
<groupId>com.google.inject</groupId>
<artifactId>guice</artifactId>
<version>5.1.0</version>
</dependency>
关键词
@Inject
有两个作用:
- 用于构造函数,表示这个构造函数是被Guice管理的,Guice会自动调用这个构造函数
- 用于成员变量,表示这个成员变量是被Guice管理的,Guice会自动注入这个成员变量
@Inject
BMW() {}
AbstractModule
是 Guice 提供的抽象类,用于创建 Guice Module- Guice Module 用于告诉 Guice 如何创建一个类的实例
final class CarModule extends AbstractModule {
// configure 方法用于配置 Guice Module
@Override
protected void configure() {
// bind 方法用于绑定一个类到一个实现类
bind(Car.class).to(BMW.class);
}
}
Guice.createInjector
方法用于创建一个 Guice Injector- 传入一个
CarModule
对象,让 Guice 把Car
绑定到BMW
实现类
Injector injector = Guice.createInjector(new CarModule());
// injector.getInstance 从 Guice 中获取一个实例
Owner owner = injector.getInstance(Owner.class);
- bind 方法用于绑定一个类到一个实现类 (常用)
bind(Car.class).to(BMW.class);
@Provides
与 @Inject
区别:
@Inject
用于构造函数和成员变量,表示这个构造函数或成员变量是被 Guice 管理的,Guice 会自动调用这个构造函数或注入这个成员变量@Provides
用于方法,表示这个方法是被 Guice 管理的,Guice 会自动调用这个方法 (常用)
@Provides
Car provideCar() {
return BMW.create();
}
@Singleton
是 Guice 提供的注解,用于标记一个类为单例类
- 单例类的实例只会被创建一次,之后每次调用都会返回同一个实例
- 好处是可以减少内存开销,但是会导致类的状态不可控
// 标记一个实例的三种方法:
// 方法一 (常用)
bind(Car.class).to(BMW.class).in(Singleton.class); // Singleton.class 保证只创建一个实例
// 方法二
bind(Car.class).toInstance(BMW.create());
// 方法三 (常用)
@Provides
@Singleton
Car provideCar() {
return BMW.create();
}
Provider
是 Guice 提供的接口,用于延迟创建对象
@Inject
Owner(Provider<Car> carProvider) {
// 懒汉模式获取对象
this.car = carProvider.get();
}
@Qualifier
是 Guice 提供的注解,用于标记一个类的实现类
- 它用于解决一个接口有多个实现类的情况
@Named
是 Guice 以前的版本提供的注解,现在已经被@Qualifier
替代
// 使用指定的实现类 CarBMW
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD})
@interface CarBMW {}
@Inject
Owner(@CarBMW Car car) { // 通过 @CarBMW 来标记
this.car = car;
}
// Provides 的时候声明 annotation 为 CarBMW (常用)
@Provides
@CarBMW
Car provideBMW() {
return BMW.create();
}
// 在 bind 的时候声明 annotation 为 CarBMW (用得少)
bind(Car.class).annotatedWith(CarBMW.class).to(BMW.class);