十分钟掌握Google Guice(上)

关注微信公众号,获取更多技术干货!
在这里插入图片描述
Google Guice(读音:juice)是从Google AdWords项目开源出来的一款轻量级的依赖注入(DI,Dependency Injection)框架。说到依赖注入框架,首先想到的应该就是Spring,也自然会有好事者,将Google Guice与Spring进行比较。其实比较的意义不大,因为Spring在国内基本上已经一统天下了,开始新写个业务模块,一般第一件事就是拷贝Spring的配置文件,拷完之后拷DAO层,拷完DAO层拷Service,最后是Controller层。外加Spring Boot、Spring Cloud全家桶,开发的确爽歪歪。
要开始比较了,难解难分呢,不知道如何做出选择呢!
要开始比较了,难解难分呢,不知道如何做出选择呢!
好吧,好吧,还是比较一下吧!要不,也对不起封面图片。

  • Guice支持Java&Annotation的配置方式,Spring支持XML&Annotation的配置方式。都会用到注解,Java和XML没有本质的区别,都是一门变成语言,不要歧视XML。╮(╯_╰)╭,毕竟PHP才是最好的编程语言。Spring的注入更加自动一些,有很多默认注入方式。

第一局Spring胜!

  • 单说依赖注入方面的学习难度,Guice还是挺烦,主要是因为资料少,宣传不够啊,可见,技术都是炒作出来的。除了官网,其他的中文资料都是入门级别,包括这篇文字。哎,其实Guice哪里特么有官网,就是GitHub。Guice,你确定你是Google开源的吗?你特么是不是弃婴啊!

第二局Spring胜!
在与其他组件的集成方面,Spring 全家桶直接吊打Guice!我只见过一个程序员用Guice写业务代码,最后他屎了,被接手他代码的人砍屎了。

第三局,Spring胜!
在这里插入图片描述
果然,Spring吊打了Guice几十年!!!
“那你还写这篇文章干啥?浪费劳资时间,关了,退了!”
“劳资也不想写啊!是农药不好喝?是吃鸡不好玩?还是直播小姐姐不好看?要不是Druid、Elastic Search、Play2里面都用了Guice作为他们的DI框架,劳资是万万不会看Google Guice的!“
在这里插入图片描述
Guice一时爽,接手死全家!Don’t bibi,show me code!
好吧,举个例子!现在有一个Service接口,其实现类是DefaultService,在另一个类(这里是DogEgg)需要一个Service实例,这个时候就需要注入了!代码如下:

public static void main(String[] args) {
    Injector injector = Guice.createInjector(new BindModule());
    DogEgg dogEgg = injector.getInstance(DogEgg.class);
    System.out.println(dogEgg.service);
}

interface Service {
}

public static class DefaultService implements Service {
}

public static class DogEgg {
    @Inject // 告诉Guice,这里要注入东西,具体的注入规则从Module里找吧
    public Service service;
}

public static class BindModule implements Module {
    @Override
    public void configure(Binder binder) {
        // 在注入的时候,遇到Service接口类型,全部注入成DefaultService实例
        binder.bind(Service.class).to(DefaultService.class);
    }
}

是不是很溜?是不是很Easy呢?
在这里插入图片描述
在这里插入图片描述
”谁家接口只有一个实现?全TM是套路!!!“
”Hello World嘛,必须是套路啊!吓得小编黑边都没截掉!!!“
Guice怎么解这个情况呢?自定义一个注解啊,代码如下:

@BindingAnnotation
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.TYPE})
public @interface DefaultService {
}

public static void main(String[] args) {
    Injector injector = Guice.createInjector(new BindModule());
    DogEgg dogEgg = injector.getInstance(DogEgg.class);
    System.out.println(dogEgg.service);
    System.out.println(dogEgg.service2);
}

interface Service {}

public static class DefaultService implements Service {}

public static class DefaultService2 implements Service {}

public static class DogEgg {
    @Inject
    @DefaultAnnotation // 使用自定义注解标识该字段需要DefaultService实例
    public Service service;

    @Inject // 没有自定义注解,那就注入DefaultService2实例
    public Service service2;
}

public static class BindModule implements Module {
    @Override
    public void configure(Binder binder) {
        binder.bind(Service.class).annotatedWith(DefaultAnnotation.class)
                .to(DefaultService.class).in(Scopes.SINGLETON);

        binder.bind(Service.class).to(DefaultService2.class);
    }
}

除了直接使用@DefaultAnnotation这个注解,还可以有更作死的注入方式:

// 这里的Key告诉Guice两件事,一个是需要一个Service接口的实例,另一个是需要DefaultAnnotation注解
// 你说Guice知道了会实例化哪个实现?!傻子都知道!
Service service3 = injector.getInstance(Key.get(Service.class,DefaultAnnotation.class));
System.out.println(service3);

// 这里的Key告诉Guice一件事,需要一个Service接口的实例,Guice自然实例化DefaultService2实例
Service service4 = injector.getInstance(Key.get(Service.class));
System.out.println(service4);

一个接口多实现的问题虽然解了,估计读者一定很想给小编一些”点心“。
在这里插入图片描述
”要是一个接口有4个实现,我要定义4个注解?靠,溜了溜了。“
”大爷别走啊!来来来,高潮还没到呢!“
Guice可以通过“名字”进行注入,代码如下:

public static void main(String[] args) {
    Injector injector = Guice.createInjector(new BindModule());
    DogEgg dogEgg = injector.getInstance(DogEgg.class);
    System.out.println(dogEgg.service);
    System.out.println(dogEgg.service2);
    System.out.println(dogEgg.service3);
}

interface Service {}

public static class DefaultService implements Service {}

public static class DefaultService2 implements Service {}

public static class DogEgg {
    @Inject
    @Named("service1") // 注意这里的@Named注解
    public Service service;

    @Inject
    @Named("service2") // 注意这里的@Named注解
    public Service service2;

    @Inject
    public Service service3; // 注意这里的没有@Named注解!!!
}

public static class BindModule implements Module {
    @Override
    public void configure(Binder binder) {
        // 被@Named("service1")修饰的,给注入DefaultService实例
        binder.bind(Service.class).annotatedWith(Names.named("service1"))
                .to(DefaultService.class);
        // 被@Named("service2")修饰的,给注入DefaultService2实例
        binder.bind(Service.class).annotatedWith(Names.named("service2"))
                .to(DefaultService2.class);
        // 默认给注入DefaultService实例
        binder.bind(Service.class).to(DefaultService.class);
    }
}

真香定律
上面这个栗子的输出瞅一眼:

DefaultService@fad74ee
DefaultService2@1a1d6a08
DefaultService@37d31475  // 和第一行的DefaultService实例不是一个?!

人家Spring可以配置单例还是原型,Guice也可以,在bind的时候指定一下就行,像上面那样不指定任何Scope的时候,每次注入时都会创建新实例!

public static class DogEgg {
    @Inject
    @Named("service1")
    public Service service;

    @Inject
    @Named("service1")
    public Service service5;

    @Inject
    @Named("service2")
    public Service service2;

    @Inject
    public Service service3;

    @Inject
    public Service service4;
}

public static class BindModule implements Module {
    @Override
    public void configure(Binder binder) {
        binder.bind(Service.class).annotatedWith(Names.named("service1"))
                .to(DefaultService.class).in(Scopes.SINGLETON); // 这里指定了Scope
        binder.bind(Service.class).annotatedWith(Names.named("service2"))
                .to(DefaultService2.class);
        binder.bind(Service.class).to(DefaultService.class).in(Scopes.SINGLETON); // 这里也指定了Scope
    }
}

输出次序为service1、service5、service2、service3、service4如下:

DefaultService@ed9d034
DefaultService@ed9d034 // service1和service5是一个对象
DefaultService2@6121c9d6
DefaultService@87f383f
DefaultService@87f383f // service3和service4是一个对象,但是与service1和service2不相同

上面这波输出也告诉我们,Guice的单例是按照binding划分的。
如果要全局单例,可以使用@Singleton注解,代码如下:

@Singleton // 这个注解告诉Guice,该实现全局单例
public static class DefaultService implements Service {
}

public static class DefaultService2 implements Service {
}

public static class DogEgg {
    @Inject
    @Named("service1")
    public Service service;

    @Inject
    @Named("service1")
    public Service service5;

    @Inject
    @Named("service2")
    public Service service2;

    @Inject
    public Service service3;

    @Inject
    public Service service4;
}

public static class BindModule implements Module {
    @Override
    public void configure(Binder binder) {
        binder.bind(Service.class).annotatedWith(Names.named("service1"))
                .to(DefaultService.class); // 这里没有再指定Scope
        binder.bind(Service.class).annotatedWith(Names.named("service2"))
                .to(DefaultService2.class);
        binder.bind(Service.class).to(DefaultService.class);  // 这里没有再指定Scope
    }
}

这里的Singleton到底是懒汉模式还是饿汉模式呢?这个也是可以配置的,默认是懒汉模式,就是延迟加载!!!其实懒汉和饿汉模式在代码跑起来之后,没啥太大差别,毕竟该创建的都创建了。要是想详细理解的话,翻翻文档吧,这个不是特别重要。
除了Singleton之外,还有Scope,例如ServletScopes.REQUEST,也去看文档吧,这个也不是特别重要。
除了上面介绍的字段注入,Guice还可以通过构造注入:

public static void main(String[] args) {
    Injector injector = Guice.createInjector(new BindModule());
    DogEgg dogEgg = injector.getInstance(DogEgg.class);
    System.out.println(dogEgg.service);
    System.out.println(dogEgg.service5);
    System.out.println(dogEgg.service2);
    System.out.println(dogEgg.service3);
    System.out.println(dogEgg.service4);
}

interface Service {}

@Singleton
public static class DefaultService implements Service {

    private String name;
    private TestArg testArg;

    public DefaultService(String name, TestArg testArg) { // 构造函数
        this.name = name;
        this.testArg = testArg;
    }

    public DefaultService() {} // 无参构造函数

    @Override
    public String toString() { // 这里专门加了一个hashCode(),用于区分是不是一个对象
        return hashCode() +" DefaultService{" +
                "name='" + name + '\'' +
                ", testArg=" + testArg +
                '}';
    }
}

public static class DefaultService2 implements Service {}

public static class DogEgg {
    @Inject
    @Named("service1")
    public Service service;

    @Inject
    @Named("service1")
    public Service service5;

    @Inject
    @Named("service2")
    public Service service2;

    @Inject
    public Service service3;

    @Inject
    public Service service4;
}

public static class TestArg {
}

public static class BindModule implements Module {
    @Override
    public void configure(Binder binder) {
        // 碰到@Named("service1")注解修饰的注入,继续走无参构造函数
        binder.bind(Service.class).annotatedWith(Names.named("service1")).to(DefaultService.class);
        binder.bind(Service.class).annotatedWith(Names.named("service2"))
                .to(DefaultService2.class);
        // binder.bind(Service.class).to(DefaultService.class); 这与下面走构造函数的binding冲突,Guice不知道该走哪个构造方法
        try {
            binder.bind(Service.class) // 没有@Named("service1")注解修饰的注入,走构造方法
                    .toConstructor(DefaultService.class
                            .getConstructor(String.class, TestArg.class));
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
    }
}

看一下输出,service1、5是同一个对象,都走了无参构造函数。service3、4为同一个对象,都走了构造方法。而且DefaultService上有@Singleton注解作妖。

926370398 DefaultService{name='null', testArg=null}
926370398 DefaultService{name='null', testArg=null}
DefaultService2@4671e53b
767010715 DefaultService{name=' ', testArg=TestArg@6950e31}
767010715 DefaultService{name=' ', testArg=TestArg@6950e31}

Guice入门的上篇就到这里了,下篇会介绍一个Provider之类的东西,这玩意类似于Spring的工厂。还可能介绍一个Apache Druid对Guice的使用。随缘介绍。。。。。。。。。。

©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页