Guice简介
Guice是Google出品的一款轻量级DI框架,Elasticsearch就使用了Guice。说到DI,离不开Spring。在Guice的Wiki上写着 While Spring is a comprehensive stack, Guice focuses on dependency injection。
Spring不是今天的主角,开始正文吧。
Guice的优点
- 速度快,号称比spring快100倍。(摘抄,但是感觉所说的快就是启动的时候,不是指运行时)
- 无外部配置(如需要使用外部可以可以选用Guice的扩展包),完全基于annotation特性,支持重构,代码静态检查。
- 简单,快速,基本没有学习成本。 个人感觉Guice有很多地方和Spring是有点像的,它的wiki中也写到他们在开发的时候是给Spring的开发团队看过Guice源码,也借鉴了一些东西。
快速开始
Guice的注入非常方便,不需要配置文件。开始demo 首先引入依赖
Gradle
compile "com.google.inject:guice:4.1.0
Maven
<dependency>
<groupId>com.google.inject</groupId>
<artifactId>guice</artifactId>
<version>4.1.0</version>
</dependency>
复制代码
// 被依赖的dao
@Singleton // 打上了这个标记说明是单例的,否则Guice每次回返回一个新的对象
public class UserDao{
public void say(){
System.out.println("dao is saying");
}
}
// service,依赖 UserDao
public class UserService {
@Inject
private UserDao mUserDao;
public void say() {
return mUserDao.say();
}
}
// 启动类
public class Start {
public static void main(final String[] args) {
//这步就是我们问Guice去要对象
final Injector injector = Guice.createInjector();
final UserService userService = injector.getInstance(UserService.class);
userService.say();
}
}
结果输出:dao is saying
复制代码
可以看到没有任何的xml配置,唯一需要做的,就是在需要注入的属性上,打上**@inject**。 使用 Guice.createInjector()启动。通常需要尽早在程序中创建注入器。这样 Guice 能够帮助您创建大部分对象 Guice中可以实现依赖注入,并且完全可以正常的运行(感觉这个破坏了分层,暂时没发现这个在什么场景会用到?)
注入
在Guice中,注入和绑定是分开的,先来讲注入。 注入的方式
- 构造函数注入 这个和Spring中的一样,如下即可
public class UserService {
private UserDao mUserDao;
@Inject
public UserService(UserDao userDao) {
this.mUserDao = userDao;
}
public void say() {
mUserDao.say();
}
}
复制代码
- 方法注入 可以理解为Spring的setter注入,但是Guice不关心这个方法的名称、访问修饰符、参数个数、返回值等,它只关心,这个方法上是否有@inject注解,比如下面这种奇怪的写法,也是可以被注入的 eg.
@Inject private Long injectDao(UserDao userDao) { this.userdao = user dao } 复制代码
- 字段注入 快速开始中就是使用这个来注入的,跳过。
绑定
这是一张Guice绑定的结构图,作为参考
上面图中引入了Module, Module是一个特殊的类,用于告诉 Guice 具体使用哪个依赖项。简单的说,用来绑定各个接口对应的实现。在之前的demo中,并没有用到Module,也成功运行了,是因为之前没有设计到接口,当只是依赖确切的实现类的时候,Guice会自动的找到需要注入的实现类
先来看看对于同一个接口的具有多个实现类的绑定,代码如下
// 先来一个接口
public interface IAnimal {
void talk();
}
// 实现类dog
public class Dog implements IAnimal {
@Override
public void talk() {
System.out.println("i am dog!");
}
}
// 实现类 SuperDog
public class SuperDog implements IAnimal {
@Override
public void talk() {
System.out.println("i am super dog");
}
}
// 这个类有点类似于Spring的Qualife
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.FIELD, ElementType.PARAMETER })
@BindingAnnotation
public @interface Super {
}
// service
public class AnimalService {
@Inject
private IAnimal dog;
//打上了@Super的注解,Guice在注入的时候会根据在Module里面配置的绑定关系注入对应的实现类
@Inject @Super
private IAnimal superDog;
public void talk() {
System.out.println("dog will talk");
dog.talk();
System.out.println("super will talk");
superDog.talk();
}
}
// 这里就是绑定逻辑的地方
public class AnimalModule extends AbstractModule {
@Override
protected void configure() {
// 这行的意思即是把Dog绑定给IAnimal
bind(IAnimal.class).to(Dog.class);
// 下面
bind(IAnimal.class).annotatedWith(Super.class).to(SuperDog.class);
}
}
// 运行
public class MainStart {
public static void main(final String[] args) {
final Injector injector = Guice.createInjector(new AnimalModule());
final AnimalService animalService = injector.getInstance(AnimalService.class);
animalService.talk();
}
运行上面的代码,最终会输出:
dog will talk
i am dog!
super will talk
i am super dog
复制代码
关键的地方只有
- IAnimal 具有两个不同的实现类 Dog 和SuperDog
- @Super 注解必须带有 @BindingAnnotation,否则Guice会报错
- AnimalModule 中重写的 configure() 方法,这里面维护了一个绑定的关系
- 如果在接口上打上@ImplementedBy(XXX.class)接口,Guice 会把 XXX.class作为该接口的默认实现类处理