当前位置: 首页 > news >正文

Guice 4.1教程

Guice是Google开发的一个开源轻量级的依赖注入框架,运行速度快,使用简单。

 

项目地址:https://github.com/google/guice/

最新的版本是4.1,本文基于此版本。

 

 

0. 什么是依赖注入?

依赖注入(Dependency Injection)是一种思想。

在一般的编程中,如果我们想要使用一个class的实例,那么必须要调用构造方法new class()手动将其实例化,如果这个class中又有对其他class属性的引用,那么在构造方法中,又要调用其他class的构造方法将其实例化。

这就是“依赖”。

Guice这种依赖注入框架的作用就是接管class之间的依赖关系,如果我们想要使用一个class的实例,Guice会自动生成实现类的实例并交给我们。

这个过程就叫做“注入”。

 

1. 利用Module的依赖注入

//定义Dog接口
public interface Dog {
    void bark();
}

//定义BlackDog实现类
public class BlackDog implements Dog{
    @Override
    public void bark() {
        System.out.println("i am black dog");
    }
}

//依赖注入
public class GuiceTest {
    public static void main(String[] args) {
        Injector injector = Guice.createInjector(new Module() {
            @Override
            public void configure(Binder binder) {
                binder.bind(Dog.class).to(BlackDog.class);
            }
        });
        Dog dog = injector.getInstance(Dog.class);
        dog.bark();
    }
}

输出结果如下

i am black dog

这个例子非常简单,先在自定义的Module中将Dog接口与BlackDog实现类关联起来。此时如果调用getInstance方法想要获取Dog接口的实例,就自动创建一个BlackDog的实例并返回。

 

2. 绑定到实例的依赖注入

public static void main(String[] args) {
        Injector injector = Guice.createInjector(new Module() {
            @Override
            public void configure(Binder binder) {
                binder.bind(Dog.class).toInstance(new BlackDog());
            }
        });

        Dog dog = injector.getInstance(Dog.class);
        dog.bark();
    }

可以看到Dog接口已经与某个BlackDog的实例绑定起来了。

 

3. 利用Provider的依赖注入

    public static void main(String[] args) {
        Injector injector = Guice.createInjector(new Module() {
            @Override
            public void configure(Binder binder) {
                binder.bind(Dog.class).toProvider(new Provider<Dog>() {
                    Dog dog = new BlackDog();

                    @Override
                    public Dog get() {
                        return dog;
                    }
                });
            }
        });

        Dog dog = injector.getInstance(Dog.class);
        dog.bark();
    }

换了一种方法的绑定,在自定义的Provider类中,我们可以执行一些复杂的操作。

 

4. 基于注解的依赖注入

可以在接口上添加@ImplementedBy完成绑定操作

@ImplementedBy(BlackDog.class)
public interface Dog {
    void bark();
}

public class BlackDog implements Dog{
    @Override
    public void bark() {
        System.out.println("i am black dog");
    }
}

public class GuiceTest {
    public static void main(String[] args) {
        Injector injector = Guice.createInjector();
        Dog dog = injector.getInstance(Dog.class);
        dog.bark();
    }
}

可以看到没有为Guice指定任何Module,也完成了依赖注入的操作

当然,也可以用@ProvidedBy注解完成绑定操作

@ProvidedBy(BlackDogProvider.class)
public interface Dog {
    void bark();
}

public class BlackDogProvider implements Provider<Dog> {
    @Override
    public Dog get() {
        return new BlackDog();
    }
}

public class GuiceTest {
    public static void main(String[] args) {
        Injector injector = Guice.createInjector();
        Dog dog = injector.getInstance(Dog.class);
        dog.bark();
    }
}

基于注解的依赖注入其实与基于Module的依赖注入差别不大,只是口味上的区别

但是Module里的配置会覆盖注解里的配置

 

5. 单例

在默认情况下,每次调用getInstance方法,都会创建一个新的对象并返回(Provider注入与实例注入除外)

@ImplementedBy(BlackDog.class)
public interface Dog {
    void bark();
}

public class BlackDog implements Dog{
    @Override
    public void bark() {
        System.out.println("i am black dog");
    }
}

public class GuiceTest {
    public static void main(String[] args) {
        Injector injector = Guice.createInjector();
        for (int i = 0; i < 10; i++) {
            System.out.println(injector.getInstance(Dog.class).hashCode());
        }
    }
}

调用10次getInstance方法,分别计算得到对象的hashCode,结果如下

1709366259
1335298403
1643691748
2068434592
143110009
2142003995
1535634836
1846412426
1539805781
1206883981

可以看出,每次调用getInstance方法,都会生成一个全新的对象。

如果我们希望返回的是同一个对象(单例模式),那么只需要在实现类上加一个@Singleton注解

@ImplementedBy(BlackDog.class)
public interface Dog {
    void bark();
}

@Singleton
public class BlackDog implements Dog{
    @Override
    public void bark() {
        System.out.println("i am black dog");
    }
}

public class GuiceTest {
    public static void main(String[] args) {
        Injector injector = Guice.createInjector();
        for (int i = 0; i < 10; i++) {
            System.out.println(injector.getInstance(Dog.class).hashCode());
        }
    }
}

运行结果如下:

1632492873
1632492873
1632492873
1632492873
1632492873
1632492873
1632492873
1632492873
1632492873
1632492873

可以看到@Singleton注解已经生效,调用getInstance方法返回的对象已经是单例的了

 

6. 属性注入

Guice可以在某个类初始化时,为它的属性自动注入实例,无需我们手动赋值

@ImplementedBy(BlackDog.class)
public interface Dog {
    void bark();
}

public class BlackDog implements Dog{
    @Override
    public void bark() {
        System.out.println("i am black dog");
    }
}

public class GuiceTest {
    @Inject
    private Dog dog;

    public static void main(String[] args) {
        Injector injector = Guice.createInjector();

        GuiceTest guiceTest = injector.getInstance(GuiceTest.class);
        guiceTest.dog.bark();
    }
}

程序的输出结果为:

i am black dog

可以看到,通过getInstance方法创建出来的GuiceTest实例中的dog属性,已经被自动注入了一个BlackDog实例。

这就是@Inject注解的效果,如果dog属性没有这个@Inject注解,程序会抛出NullPointerException

 

7. 构造函数注入

如果在构造函数上添加@Inject注解,那么可以实现在对象初始化的时候自动提供参数,代码如下

public class GuiceTest {
    private Dog dog;

    @Inject
    public GuiceTest(Dog dog) {
        this.dog = dog;
    }

    public static void main(String[] args) {
        Injector injector = Guice.createInjector();

        GuiceTest guiceTest = injector.getInstance(GuiceTest.class);
        guiceTest.dog.bark();
    }
}

在调用getInstance获取GuiceTest实例的时候,会自动调用唯一的GuiceTest构造方法并传入参数

需要注意的是,此时必须有且只有一个带有@Inject注解的构造方法,否则会抛出类似如下的异常:

GuiceTest has more than one constructor annotated with @Inject. Classes must have either one (and only one) constructor annotated with @Inject or a zero-argument constructor that is not private.

 

8. Setter注入

与之前提到的属性注入非常类似,只需要在setter方法上添加一个@Inject注解即可。

public class GuiceTest {
    private Dog dog;

    public Dog getDog() {
        return dog;
    }

    @Inject
    public void setDog(Dog dog) {
        this.dog = dog;
    }

    public static void main(String[] args) {
        Injector injector = Guice.createInjector();

        GuiceTest guiceTest = injector.getInstance(GuiceTest.class);
        guiceTest.dog.bark();
    }
}

 

9. 静态变量注入

上面的注入都是针对某个类的非静态属性进行注入,如果我们对某个类的静态属性进行注入,会发生什么呢?

public class GuiceTest {
    @Inject private static Dog dog;

    public static void main(String[] args) {
        Injector injector = Guice.createInjector();

        GuiceTest guiceTest = injector.getInstance(GuiceTest.class);
        guiceTest.dog.bark();
    }
}

程序的执行结果是:

Exception in thread "main" java.lang.NullPointerException

很显然,注入失败了。因为getInstance方法是针对某个类的实例进行注入,而类的静态属性是被这个类的所有对象全局共享的,所以直接注入会失败。

正确的注入姿势如下:

public class GuiceTest {
    @Inject private static Dog dog;

    public static void main(String[] args) {
        Injector injector = Guice.createInjector(new Module() {
            @Override
            public void configure(Binder binder) {
                binder.requestStaticInjection(GuiceTest.class);
            }
        });
        
        GuiceTest.dog.bark();
    }
}

程序的执行结果如下:

i am black dog

注入成功!

虽然目前还不知道具体的实现原理,但是可以猜出Guice一开始就直接往GuiceTest的静态变量里注入了对象。

 

10. 直接注入属性

还有一种简单暴力的方式来完成属性的依赖注入

public class GuiceTest {
    @Inject private Dog dog;

    public static void main(String[] args) {
        Injector injector = Guice.createInjector();

        GuiceTest guiceTest = new GuiceTest();
        injector.injectMembers(guiceTest);
        guiceTest.dog.bark();
    }
}

对GuiceTest的实例直接调用injectMembers方法,完成对实例的属性的依赖注入

 

11. 单接口多实例

如果一个接口有多个实现类,Guice提供了一种非常灵活的注入方案

public interface Dog {
    void bark();
}

public class BlackDog implements Dog{
    @Override
    public void bark() {
        System.out.println("i am black dog");
    }
}

public class WhiteDog implements Dog {
    @Override
    public void bark() {
        System.out.println("i am white dog");
    }
}

@BindingAnnotation
@Target({FIELD, PARAMETER, METHOD})
@Retention(RUNTIME)
public @interface Black {
}

@BindingAnnotation
@Target({FIELD, PARAMETER, METHOD})
@Retention(RUNTIME)
public @interface White {
}

public class GuiceTest {
    @Inject @Black private Dog dog;

    public static void main(String[] args) {
        Injector injector = Guice.createInjector(new Module() {
            @Override
            public void configure(Binder binder) {
                binder.bind(Dog.class).annotatedWith(White.class).to(WhiteDog.class);
                binder.bind(Dog.class).annotatedWith(Black.class).to(BlackDog.class);
            }
        });

        GuiceTest guiceTest = new GuiceTest();
        injector.injectMembers(guiceTest);
        guiceTest.dog.bark();
    }
}

运行结果如下:

i am black dog

额外定义了@Black与@White注解,然后在Module里定义了两条规则,这样被@Black注解修饰的dog属性就被注入了BlackDog的依赖。

非常灵活,在特定场景下十分有用。

比方说可能实现类分为Debug与Release两类,调试的时候所有待注入的属性都用@Debug注解修饰,发布前将所有@Debug替换成@Release即可。

 

12. @Named注解

如果懒得额外定义注解,那么可以直接使用Guice提供的@Named注解完成相似的功能。

public class GuiceTest {
    @Inject @Named("Black") private Dog dog;

    public static void main(String[] args) {
        Injector injector = Guice.createInjector(new Module() {
            @Override
            public void configure(Binder binder) {
                binder.bind(Dog.class).annotatedWith(Names.named("White")).to(WhiteDog.class);
                binder.bind(Dog.class).annotatedWith(Names.named("Black")).to(BlackDog.class);
            }
        });

        GuiceTest guiceTest = new GuiceTest();
        injector.injectMembers(guiceTest);
        guiceTest.dog.bark();
    }
}

可以看到@Named注解也能完成指定实现类的关联工作

 

 

总结

本文简单介绍了Guice这个轻量级的依赖注入框架的基本使用方法。

虽然简单,但是也展现了依赖注入思想的精华之处:由容器来解决依赖对象的构建问题,程序员只需要提供属性的关联关系即可。使得程序松耦合,减少代码之间的依赖关系,便于后续的修改。

更详细的信息,请参见官方Wiki

转载于:https://www.cnblogs.com/stevenczp/p/7326953.html

相关文章:

  • CSS样式表中a:link,a:visited,a:hover,a:active属性含义
  • 64款工具,总有合适您的那款
  • XP怎样开放电脑端口,如:5001
  • 一个谈创业的作者的文集,有点意思
  • ASP的网站配置:请验证实例名称是否正确并且 SQL Server 已配置为允许远程连接。...
  • Java常用的非受检异常
  • oraToolKit之一安装oraToolKit并检测安装oracle环境
  • 提高sqlmap爆破效率
  • 让win7下的U盘变快!
  • 按钮级别权限管理数据库设计及后台接口实现
  • jquery 调整高度
  • 使用Apache JMeter压測Thrift
  • 各种语系的简称(判断浏览器是中文版还是英文版)
  • Floyd判圈算法
  • 奇怪的错误----在firefox中的帧页面源码中找不到, 但是保存成txt之后找得到
  • 《深入 React 技术栈》
  • 0x05 Python数据分析,Anaconda八斩刀
  • bootstrap创建登录注册页面
  • CSS3 变换
  • Git初体验
  • JavaScript异步流程控制的前世今生
  • JS变量作用域
  • Mac转Windows的拯救指南
  • Mocha测试初探
  • Quartz实现数据同步 | 从0开始构建SpringCloud微服务(3)
  • React-Native - 收藏集 - 掘金
  • spring boot 整合mybatis 无法输出sql的问题
  • VirtualBox 安装过程中出现 Running VMs found 错误的解决过程
  • vue从入门到进阶:计算属性computed与侦听器watch(三)
  • 看完九篇字体系列的文章,你还觉得我是在说字体?
  • 前端之Sass/Scss实战笔记
  • 让你的分享飞起来——极光推出社会化分享组件
  • 小李飞刀:SQL题目刷起来!
  • 一份游戏开发学习路线
  • 移动端解决方案学习记录
  • 如何在 Intellij IDEA 更高效地将应用部署到容器服务 Kubernetes ...
  • #【QT 5 调试软件后,发布相关:软件生成exe文件 + 文件打包】
  • (1)(1.11) SiK Radio v2(一)
  • (1)(1.13) SiK无线电高级配置(六)
  • (DenseNet)Densely Connected Convolutional Networks--Gao Huang
  • (Oracle)SQL优化技巧(一):分页查询
  • (Python第六天)文件处理
  • (zt)基于Facebook和Flash平台的应用架构解析
  • (二)c52学习之旅-简单了解单片机
  • (附源码)ssm高校实验室 毕业设计 800008
  • (一)VirtualBox安装增强功能
  • (转)Google的Objective-C编码规范
  • .Net CF下精确的计时器
  • .net core 依赖注入的基本用发
  • .Net FrameWork总结
  • .Net7 环境安装配置
  • .NET序列化 serializable,反序列化
  • .sys文件乱码_python vscode输出乱码
  • @DependsOn:解析 Spring 中的依赖关系之艺术
  • @取消转义