Google Guice超轻量级依赖注入容器

news2024/9/20 5:35:22

Google Guice是Goolge开源的一款超轻量级依赖注入容器,超轻量的特点主要体现在:可与其它依赖注入容器混合使用,例如和Spring/Spring Boot的IOC容器混合使用;不需要任何配置信息,只需要引入几个有限的依赖包即可以使用;使用简单,只需要掌握几个有限的注释就可以使用;相对更重的依赖注入容器,Google Guice运行速度更快,消耗更少的系统资源。
在这里插入图片描述
(图片来源于网络)

实际上大多数技术人员都在不知不觉地使用Google Guice,这是因为很多工具性的组件和中间件内部都通过Google Guice进行实例管理,诸如:Elasticsearch、Netty等。本文试图通过示例和知识点讲解结合的方式,向读者介绍Google Guice的最基本使用。

1、典型使用

我们先来看一个最简单的示例,这个实例包括了相当一部分在后文将要介绍的Google Guice知识点:

// 首先我们按照正常的编码流程,定义一个普通的接口和普通的实现类
public interface OrderService {
  public void doSomething();
}

// ============================
// 以下是OrderService的一个普通实现
public class OrderServiceImpl implements OrderService {
  @Override
  public void doSomething() {
    System.out.println(" ===== doSomething ===== ");
  }
}

// ============================
// 然后我们将OrderService接口和OrderServiceImpl实现类的绑定关系,注册到依赖注入容器中
// 这个过程是通过一个Module的概念进行设计的
public class PlayerModule implements Module {
  @Override
  public void configure(Binder binder) {
    // .....
    binder.bind(OrderService.class).to(OrderServiceImpl.class);
    // .....
  }
}

// ============================
// 最后就可以通过依赖注入管理器,来进行使用了
// 只是一个Google Guice最简单的使用
public static void main(String[] args) {
  // 加载各个配置模块,module是Google Guice的一个概念,里面实际上是容器的实例映射信息
  Injector injector = Guice.createInjector(new Module[] {new PlayerModule()});
  // 从依赖容器中取得实例
  OrderService orderService = injector.getInstance(OrderServiceImpl.class);
  // 开始调用具体的工作方法
  orderService.doSomething();
}
// ......
  • Injector:是Google Guice中的一个概念,是依赖注入容器(管理器),一个应用程序中可以根据实际情况创建一个或者多个依赖注入容器(管理器)。

  • Module:是Google Guice中的另一个概念,可以看作模块配置信息。其中设定了依赖注入容器中实例对象和接口、实例对象和创建器等在依赖注入容器中的绑定关系。以上代码中,我们基于一个Module创建了一个依赖注入容器(管理器)。

  • 除了Google Guice专有的几个概念外,对于OrderService接口和它的实现 OrderServiceImpl上,并不需要增加特别的方法、属性或者注解。

2、Google Guice中的单例

和大家经常使用的Spring IOC容器相比,Guice依赖注入容器最需要注意的是,容器中的实例默认情况下并不是单例模式,而是多例模式。如果要求注入容器中的实例是单例模式,就需要在对应的Module中进行设定,或者通过@Singleton注解进行设定。 以下代码是在Module中绑定注册时进行单例模式的设定:

public class XXXXX implements Module {
  @Override
  public void configure(Binder binder) {
	// ......
    // asEagerSingleton()方法,就是设置成单例模式
    binder.bind(Service.class).annotatedWith(Names.named("one")).to(OneService.class).asEagerSingleton();
    // ......
  }
}

以下是通过注解方式进行单例模式的设定,注意@Singleton注解一定要加在直接注册到Module中的具体类上,如果@Singleton注解写在OrderServiceImpl类上,但是在Module设定时却描述为一个Provider(后文会讲),那么@Singleton注解将不再起作用:

// 使用@Singleton注解,表示单例
@Singleton
public class OneService implements Service {
  //  类中的各种内容可以忽略
}

3、Google Guice中的创建器

在很多情况下,技术人员需要在依赖注入容器创建一个实例时,加入一些特定的逻辑。也就是说需要干预依赖注入容器创建实例的过程。这时技术人员就需要通过设定一个Provider来完成特定实例在创建时的干预过程,如下所示:

// 注意,如果使用Provider,那么加载在World上面的Singleton注释会失效
public class WorldProvider implements Provider<World> {
  @Override
  public World get() {
    // 这是创建World类实例对象的过程
    // 具体代码忽略
    World world = new World();
    // ......
    return world;
  }
}

以上Provider的代码很好理解,就是描述一个具体的对象实例应该怎么进行创建。在设定完成后,Module中的相关设定方式也要进行调整,如下所示:

public class XXXXX implements Module {
  @Override
  public void configure(Binder binder) {
	// ......
	// 将这个Provider注册到容器
    binder.bind(World.class).toProvider(new WorldProvider())
    // ......
  }
}

4、Google Guice中的默认实现和多个实现

有的情况下,应用程序中一个接口可能有多个实现,并且这些实现都需要注入到容器中进行管理。那么这些实现就需要指定一个默认实现,这样才能保证注入管理器(Injector)能够正常工作。Google Guice主要提供了两种注解方式来为接口指定默认的实现方式。分别是@ImplementedBy注解和@ProvidedBy注解,示例代码如下:

// 使用@ImplementedBy注解,说明这个接口的默认实现是OneService
@ImplementedBy(OneService.class)
public interface Service {
  public void doSomething();
  public void doOther();
}

// 或者,使用@Provided注解,说明这个接口默认的创建器是WorldProvider
@ProvidedBy(WorldProvider.class)
public interface OrderService {
  public void doSomething();
}

@ImplementedBy注解加载到Service接口上,说明这个接口的默认实现为OneService类;@ProvidedBy注解加载到Service接口上,说明这个接口的默认实例创建的工作,由WorldProvider负责完成。当一个接口拥有多个实现时,除了为这个接口指定默认实现外,还需要为注入容器的多个实现分别指定辨识名。这个辨识名可以使用Google Guice提供的@Names注解,也可以使用Google Guice提供的注解扩展@BindingAnnotation。以下是给出的示例:

public class XXXXX implements Module {
  @Override
  public void configure(Binder binder) {
	// ......
    // 通过一个别名绑定接口和它的实现
    // 分别给Service接口的两个实现,设定了别名“one”和“two”
    // 别名设置为“one”的具体实现OneService,还设定了以单例模式工作
    binder.bind(Service.class).annotatedWith(Names.named("one")).to(OneService.class).asEagerSingleton();
    binder.bind(Service.class).annotatedWith(Names.named("two")).to(TwoService.class);
    // ......
  }
}

// ========= 那么从依赖注入容器中获取对象实例使用时,可以这样来编码
// 这只是一种获取方式,后文还会介绍另外一种
@Named("one")
Service service = injector.getInstance(Service.class);

以上代码为@Named注解的使用,包括Module中设定一个接口的多个实现时,如何为每个实现设置不同的名字,以及在获取实例时如何使用@Names注解获取特定的实现。还有一种方式是使用@BindingAnnotation注解创建多个新的注解,来标识不同的实现。具体示例如下:

// =========== 
// 首先我们定义两个注解One和Two
// 主要关注@BindingAnnotation注解,这个注解表明,One注解可以用来标识和区分具体的实现类
@BindingAnnotation
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.LOCAL_VARIABLE)
public @interface One {

}

// 主要关注@BindingAnnotation注解,这个注解表明,Two注解可以用来标识和区分具体的实现类
@BindingAnnotation
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.LOCAL_VARIABLE)
public @interface Two {

}

// =========== 
// 接着我们可以使用这两个注解,区分一个接口的两个不同实现
public class XXXXX implements Module {
  @Override
  public void configure(Binder binder) {
	// ......
    // 通过两个注解区分Service接口的两个不同实现
    binder.bind(Service.class).annotatedWith(One.class).to(OneService.class);
    binder.bind(Service.class).annotatedWith(Two.class).to(TwoService.class);
    // ......
  }
}

// ===========
// 最后,从依赖注入容器中获取对象实例使用时,可以这样来编码
// 这只是一种获取方式,后文还会介绍另外一种
@One
Service service = injector.getInstance(Service.class);

以上代码通过@BindingAnnotation创建了两个新的注解,分别是@One和@Two,然后在Module注册该接口的不同实现时,为不同实现分别绑定不同的注解。最后在获取实例时,使用已经绑定的不同注解,就可以获取对应的实例了。Google Guice的依赖注入容器(管理器)也提供了方法,可以让开发者中取得已注册到容器中的,某一个接口的所有实现。示例如下:

// ......
List<Binding<Service>> serviceBindings = injector.findBindingsByType(TypeLiteral.get(Service.class));
// 通过findBindingsByType方法,开发人员可以获取Service接口中的一个或者多个实现
// ......

5、Google Guice 中的Module

在以上各小节中,为了向读者介绍相关知识点,实际上本文已经或多或少涉及了Google Guice中的Module使用方式。本节我们主要进行Module设定方式的总结。

5.1、绑定接口和类

Google Guice可以注册到依赖注入容器的类型可以很多,除了我们一般知道的接口以外,普通的类也可以注入到容器中。代码片如下所示:

public class XXXXX implements Module {
  @Override
  public void configure(Binder binder) {
	// ......
    // 注册普通的类到容器
    binder.bind(World.class);
    // ......
  }
}

除此以外,Google Guice可以使用以下很多种方式绑定接口和它的实现:

public class XXXXX implements Module {
  @Override
  public void configure(Binder binder) {
	// ......
    // 通过一个别名绑定接口和它的实现
    binder.bind(Service.class).annotatedWith(Names.named("one")).to(OneService.class);
    // 通过自定义注解绑定接口和它的实现
    binder.bind(Service.class).annotatedWith(One.class).to(OneService.class);
    // 直接绑定一个接口和它的实现
    binder.bind(Service.class).to(OneService.class);
    // ......
  }
}

5.2、绑定实例

除了绑定类以外,Google Guice还可以将一个已经完成实例化的对象,或者一个实例化过程注册到容器中,代码实例如下:

public class XXXXX implements Module {
  @Override
  public void configure(Binder binder) {
	// ......
    // 可以直接注入一个实例对象到容器中
    OrderService orderService = new OrderServiceImpl();
    binder.bind(OrderService.class).toInstance(orderService);
    // 也可以是一个实例化过程
    binder.bind(OrderService.class).toInstance(new OrderService() {
      @Override
      public void doSomething() {
        // 这个实例创建的创建过程
        // ........
      }
    });
    // ......
  }
}

通过这种注册方式,开发人员也是可以干预某个实例对象的创建过程的。但就本文来说,还是推荐使用创建器的方式进行实例对象创建过程的干预。

5.3、绑定创建器

可以使用一下方式向依赖注入容器注册一个创建器,以便在某个特定实例对象创建时干预对象的创建过程:

public class XXXXX implements Module {
  @Override
  public void configure(Binder binder) {
	// ......
    // 注意,这里还设定了实例对象以单例模式工作
    binder.bind(World.class).toProvider(new WorldProvider()).asEagerSingleton();
    // ......
  }
}

另外,Google Guice还提供了@Provides注解,来设定一个实例对象的创建方法。@Provides注解是放置在方法上的一个注解,并且这个方法必须属于某一个Module。示例代码如下所示:

public class XXXXX implements Module {
  @Override
  public void configure(Binder binder) {
	// 这个方法可以进行其他类型的绑定
  }
  
  // ......
  
  // 这个方法必须放置在某个Module中
  // 直接设定了如果创建OrderService接口的实例
  @Provides
  public OrderService one() {
    return new OrderServiceImpl();
  }
}

5.4、甚至可以绑定常量

一个固定的字符串当然可以进行注入:

public class XXXXX implements Module {
  @Override
  public void configure(Binder binder) {
	// ......
    binder.bind(String.class).toInstance("这是一个固定的字符串");
    // ......
  }
}

5.5、通过@Inject注解使用已注入的实例

以上本文介绍的内容,都是向依赖注入容器进行各种形式注册的示例,那么怎么进行容器中对象实例的使用呢?Google Guice提供了两种使用容器中实例对象的方式,一种是上文出现过的依赖注入管理器(Injector);另一个是使用@Inject注解。代码实例如下:

// ......
// 加载各个配置模块,module是Google Guice的一个概念,里面实际上是容器的实例映射信息
// 得到一个依赖注入容器的管理器
Injector injector = Guice.createInjector(new Module[] {new PlayerModule()});
// 然后从依赖诸如容器中取得实例
OrderService orderService = injector.getInstance(OrderServiceImpl.class);
// ......

以上这种使用依赖注入管理器的方式并不适合大多数编码场景,因为不可能在所有场景下都先获取一个依赖注入管理器,再获取实例对象。更多的场景中,我们使用的是@Inject注解。@Inject注解可以设定在某个属性上,也可以设定在某个构造函数上。如下所示:

public class XXXXXX {
  // 加载在属性上
  @Inject
  private OrderService orderService;
  // 如果一个接口有多个实现类,并使用自定义注解进行标识,则这里可以这样使用
  @One
  private Service service;
  // 如果一个接口有多个实现类,并使用别名进行标识,则这里可以这样使用
  @Named("one")
  private Service service;

  // @Inject还可以加载在构造函数上,这时Google Guice会自动进行入参的匹配(如果有入参的话)
  // 当然,如果无法明确参数中具体的实现,还可以使用自定义注解或者别名注解进行说明
  @Inject
  public XXXXXX(String value , Service service);
  
  // ......
}

之所以介绍Google Guice,是因为在一篇专栏中其中的一块实战示例会使用这个轻量级依赖注入框架。当然Google Guice也可以应用在很多组件或者小型应用工程中。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1794997.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

容器化实践:DevOps环境下的容器交付流程

DevOps的兴起是为了应对市场和消费者对技术应用的不断增长的需求。它的目标是构建一个更快的开发环境&#xff0c;同时保持软件的高质量标准。DevOps还致力于在敏捷开发周期中提升软件的整体品质。这一目标的实现依赖于多种技术、平台和工具的综合运用。 结合容器化技术与DevO…

echarts学习: 在图表中添加多条y轴会怎么样?

前言 在撰写如何绘制双y轴图表文章时&#xff0c;我突然萌生出了一个想法&#xff0c;如果给图表添加两个以上的y轴会怎么样呢? 带着这个问题我开始了自己的探索之旅。 我找到了一篇优秀的文章作为参考&#xff0c;虽然它需要付费&#xff0c;但是不要紧&#xff0c;文中免费…

达摩院重大“遗产”!fluxonium量子比特初始化300纳秒且保真度超过99%

通用量子计算机开发的主要挑战之一是制备量子比特。十多年来&#xff0c;研究人员在构建量子计算机的过程中主要使用了transmon量子比特&#xff0c;这也是迄今为止商业上最成功的超导量子比特。 但与业界多数选择transmon量子比特不同&#xff0c;&#xff08;前&#xff09;…

关于浔川python社在全网博主原力月度排名泸州地区排名第三名的调查——浔川总社部

6月6日&#xff0c;浔川python社在全网博主原力月度排名泸州地区排名第三名。 引起了广大python爱好者等的不满&#xff0c;为什么浔川python社这么一个大的python社排名第三&#xff1f; 2024-06-04 21:59:52 浔川python社在全网博主原力月度排名泸州地区排名第一名。 2024-…

基于Vue3的Uniapp实训项目|一家鲜花店

基于Vue的Uniapp实训指导项目 项目预览&#xff1a; 在这里插入图片描述 pages.json {"pages": [ //pages数组中第一项表示应用启动页&#xff0c;参考&#xff1a;https://uniapp.dcloud.io/collocation/pages{"path": "pages/index/index",&…

static的用法

static一般用于修饰局部变量&#xff0c;全局变量&#xff0c;函数 1 static修饰局部变量 是因为改为static int a1;后&#xff0c;出了作用域&#xff0c;不会销毁a的值&#xff0c;想要理解其本质&#xff0c;首先先看一下这个图&#xff1a; static修饰局部变量时&#xf…

每天一道大厂SQL题【Day32】按消息量给广东省qq打标记

文章目录 每天一道大厂SQL题【Day32】按消息量给广东省qq打标记每日语录第32题 需求三&#xff1a;按消息量给广东省qq打标记思路分析附表 答案获取加技术群讨论文末SQL小技巧 后记 每天一道大厂SQL题【Day32】按消息量给广东省qq打标记 大家好&#xff0c;我是Maynor。相信大…

RabbitMQ系列-rabbitmq无法重新加入集群,启动失败的问题

当前存在3个节点&#xff1a;rabbitmq5672、rabbitmq5673、rabbitmq5674 当rabbitmq5673节点掉线之后&#xff0c;重启失败 重启的时候5672节点报错如下&#xff1a; 解决方案 在集群中取消失败节点 rabbitmqctl forget_cluster_node rabbitrabbitmq5673删除失败节点5673的…

工欲善其事必先利其器——IntelliJ IDEA神器使用技巧

1.IntelliJ IDEA神器使用技巧【时长2小时20分】 程序员每日都会花费数小时使用ide编写和调试代码&#xff0c;其中很多操作都是机械重复且频率非常高&#xff0c;本着"工欲善其事必先利其器"的精神&#xff0c;闷头写代码之外花点时间研究一下自己用的ide&#xff0…

zookeeper启动(一)

1.zookeeper启动入口 在zkServer.sh的启动命令中,我们可以找到zookeeper启动的关键类org.apache.zookeeper.server.quorum.QuorumPeerMain QuorumPeerMain#main 我们可以直接看org.apache.zookeeper.server.quorum.QuorumPeerMain中的main方法,从下面的main方法中,我们可以…

JavaSE基础语法合集

随着不断学习&#xff0c;我们已经走完了JavaSE基础语法的所有内容&#xff0c;博主的单独语法篇共十二篇&#xff0c;感兴趣的也可以去看看&#xff0c;内容基本一致&#xff0c;目录是重新排布的&#xff0c;数组和方法都在初识Java章节。 适合&#xff1a;老手复习和新手从零…

Ubuntu系统升级k8s节点的node节点遇到的问题

从1.23版本升级到1.28版本 node节点的是Ubuntu系统20.04的版本 Q1 node节点版本1.23升级1.28失败 解决办法&#xff1a; # 改为阿里云镜像 vim /etc/apt/sources.list.d/kubernetes.list# 新增 deb https://mirrors.aliyun.com/kubernetes/apt/ kubernetes-xenial main# 执…

pdf处理命令合集

安装weasyprint用于生成pdf 单个文件合成多个pdf linux - Merge / convert multiple PDF files into one PDF - Stack Overflow

深入解析Java中volatile关键字

前言 我们都听说过volatile关键字&#xff0c;也许大家都知道它在Java多线程编程编程中可以减少很多的线程安全问题&#xff0c;但是会用或者用好volatile关键字的开发者可能少之又少&#xff0c;包括我自己。通常在遇到同步问题时&#xff0c;首先想到的一定是加锁&#xff0…

【AI基础】第二步:安装AI运行环境

开局一张图&#xff1a; ​ 接下来按照从下往上的顺序来安装部署。 规则1 注意每个层级的安装版本&#xff0c;上层的版本由下层版本决定 比如CUDA的版本&#xff0c;需要看显卡安装了什么版本的驱动&#xff0c;然后CUDA的版本不能高于这个驱动的版本。 这个比较好理解&…

520表白神器

&#x1f341;博主简介&#xff1a; &#x1f3c5;云计算领域优质创作者 &#x1f3c5;2022年CSDN新星计划python赛道第一名 &#x1f3c5;2022年CSDN原力计划优质作者 &#x1f3c5;阿里云ACE认证高级工程师 &#x1f3c5;阿里云开发者社区专…

Ripple:使用Wavelet Approximations来加速FHE的Programmable Bootstraps

1. 引言 University of Delaware和Nillion团队的 Charles Gouert、Mehmet Ugurbil、Dimitris Mouris、Miguel de Vega 和 Nektarios G. Tsoutsos&#xff0c;2024年论文《Ripple: Accelerating Programmable Bootstraps for FHE with Wavelet Approximations》&#xff0c;开源…

如何理解与学习数学分析——第二部分——数学分析中的基本概念——第9章——可积性

第2 部分&#xff1a;数学分析中的基本概念 (Concepts in Analysis) 9. 可积性(Integrability) 本章讨论了可积性(integrability)的概念(它不同于积分过程)。研究了反导数(antiderivative&#xff0c;或称原函数)和函数图像下面积之间的关系&#xff0c;然后通过对面积的近似…

使用Python绘制南丁格尔图(玫瑰图)

使用Python绘制南丁格尔图&#xff08;玫瑰图&#xff09; 南丁格尔图效果代码 南丁格尔图 南丁格尔图&#xff08;Nightingale Rose Chart&#xff09;&#xff0c;也被称为玫瑰图或极区图&#xff0c;是一种特殊的圆形统计图&#xff0c;用于显示多个类别的数据。它是由弗洛…

用蒙特卡罗积分法近似求解定积分的值及举例

一、背景知识 1、连续随机变量的概率密度函数 对于连续型随机变量的概率密度函数&#xff08;PDF&#xff09;&#xff0c;其在整个定义域上的积分必须等于1。这是概率密度函数的一个基本属性&#xff0c;它确保了随机变量取任何值的概率之和等于1&#xff0c;符合概率论的公…