Google Guice 4:Bindings(2)

news/2024/4/24 18:48:32/文章来源:https://blog.csdn.net/u014454538/article/details/129172019

4 Scopes (实例的作用域)

4.1 默认规则:unreuse instance

  • 到目前为止,通过bind().to()@Provides定义的binding,每次需要注入实例对象时,Guice都会创建一个新的实例

    // 修改DatabaseTransactionLog,使其打印自身的hash code
    @Override
    public void log(String msg) {System.out.printf("%s@%s: %s\n", this.getClass().getSimpleName(), Integer.toHexString(hashCode()), msg);
    }
    // 创建多个MyDatabase实例,需要多次注入TransactionLog
    MyDatabase database1 = injector.getInstance(MyDatabase.class);
    database1.createTable("olap.users");MyDatabase database2 = injector.getInstance(MyDatabase.class);
    database2.createTable("presto.queries");
    
  • 执行上述代码,发现Guice为MyDatabase注入了不同的DatabaseTransactionLog实例

  • 这是Guice的默认规则,Guice returns a new instance each time it supplies a value.

4.2 build-in scopes

  • 对象的生命周期可以是应用程序级别的、session级别的、request级别,通过**作用域(scope)**可以配置对象的生命周期,从而实现对象的复用

单例@Singleton

  • 在实际应用场景中,需要将类定义为单例模式

  • 例如,在PrestoDB中,与属性配置有关的类(SystemSessionProperties)、监控各种事件的类(GcMonitor)、任务调度有关的类(NodeScheduler)等都被设置为单例

  • Guice支持两种@Singletonjavax.inject.Singletoncom.google.inject.Singleton,官方建议使用javax.inject.Singleton,因为注入框架也支持它

  • 使用@Singleton修饰DatabaseTransactionLog,将其定义为单例

    @Singleton
    public class DatabaseTransactionLog implements TransactionLog {/* everything here should be threadsafe! */
    }
    
  • 重新执行上述代码,发现Guice为MyDatabase注入的是一个实例

@RequestScoped

  • 除了@Singleton这个内置的scope,Guice官方文档还提到了@RequestScoped,servlet extension所包含的、用于web应用程序的scope

  • Guice并未说明@RequestScoped的详细信息,推测是com.google.inject.servlet.RequestScoped,可以通过如下maven依赖进行引入

    <dependency><groupId>com.google.inject.extensions</groupId><artifactId>guice-servlet</artifactId><version>5.0.1</version>
    </dependency>
    

4.3 如何使用scope

  • 方法一: 在实现类上直接使用@Singleton

    @Singleton
    public class DatabaseTransactionLog implements TransactionLog {/* everything here should be threadsafe! */
    }
    
  • 方法二: 在bind()语句中定义单例

    // 1. 使用注解定义单例
    bind(TransactionLog.class).annotatedWith(Names.named("database")).to(DatabaseTransactionLog.class).in(Singleton.class);
    // 对应的in()方法
    void in(Class<? extends Annotation> scopeAnnotation);// 2. 实用Scopes.SINGLETON定义单例 
    bind(TransactionLog.class).annotatedWith(Names.named("database")).to(DatabaseTransactionLog.class).in(Scopes.SINGLETON);
    // 对应的in()方法
    void in(Scope scope);
    
  • 方法三:@Provides方法上定义单例

    @Provides
    @Named("database")
    @Singleton
    public TransactionLog providerDatabaseTransactionLog(DatabaseTransactionLog log) {return log;
    }@Provides
    @Named("database")
    @Singleton
    public TransactionLog providerDatabaseTransactionLog() {return new DatabaseTransactionLog();
    }
    
  • 上述三种定义单例的方式,最终使得Guice只会创建一个DatabaseTransactionLog对象

4.4 scopes apply to the binding source

  • 在linked binding中,scope是应用在binding source上,而非binding target

    In linked bindings, scopes apply to the binding source, not the binding target.

  • 与其说作用在binding source上,更易理解的是:作用在整个binding上

以上面@Provides定义的linked binding为例:

  • binding source:TransactionLog,binding target:DatabaseTransactionLog
  • 感觉@Singleton不会起作用,因为TransactionLog —> DatabaseTransactionLog,最终是需要得到DatabaseTransactionLog对象
  • 而无论是通过方法入参传入,还是通过new DatabaseTransactionLog()创建,而@Singleton标记不是的DatabaseTransactionLog,Guice应该会创建多个DatabaseTransactionLog对象
  • 实际上,Guice只会创建一个DatabaseTransactionLog对象,这是由于Singleton作用在TransactionLog上,使得TransactionLog只对应一个DatabaseTransactionLog对象
  • 这个DatabaseTransactionLog对象一旦创建好,Guice就像是直接从缓存中获取一样,而无需再创建新的DatabaseTransactionLog对象

示例2:一个类实现了两个接口,最终创建几个对象?

  • 小学生Pupil类,同时实现了Person和Student接口

    public interface Person {void eat();
    }public interface Student {void study();
    }public class Pupil implements Person, Student {@Overridepublic void eat() {System.out.println("A pupil is eating potato chips");}@Overridepublic void study() {System.out.println("A pupil is studying math");}@Overridepublic String toString() {return this.getClass().getSimpleName() + "@" + Integer.toHexString(this.hashCode());}
    }	
    
  • 在Module中定义binding关系

    bind(Person.class).to(Pupil.class).in(Scopes.SINGLETON);
    bind(Student.class).to(Pupil.class).in(Singleton.class);
    
  • 从Guice获取实例:

    Student student = injector.getInstance(Student.class);
    Person person = injector.getInstance(Person.class);
    System.out.printf("student: %s, person: %s", student, person);
    
  • 执行代码,发现Student对应的Pupil对象和Person对应的Pupil对象,并非同一个

  • 如果Singleton作用在binding target上,则最终将从Guice获得同一个Pupil对象;但是,Singleton作用在binding source上,使得Student和Person将对应不同的Pupil对象

如何解决上述问题?

  • 若我们希望Pupil是单例,可以在定义Pupil类时使用@Singleton

    @Singleton
    public class Pupil implements Person, Student {}
    
  • 或者添加新的binding,将Pupil定义为单例

    bind(Pupil.class).in(Singleton.class);
    

4.5 单例的懒汉模式 vs 恶汉模式

  • 恶汉模式的单例(Eager singleton)会在类加载后完成初始化,以保证用户获得一致且快速的体验

  • 懒汉模式的单例(Lazy singleton,第一次使用时才进行初始化,可以加快程序的编译、运行周期

  • Guice中定义单例的方式很多,具体为哪种模式,可以参考表格

    定义方式PRODUCTIONDEVELOPMENT
    .asEagerSingleton()eagereager
    .in(Singleton.class)eagerlazy
    .in(Scopes.SINGLETON)eagerlazy
    @Singletoneagerlazy

Guice comes with a built-in @Singleton scope that reuses the same instance during the lifetime of an application within a single injector. Both javax.inject.Singleton and com.google.inject.Singleton are supported by Guice, but prefer the standard javax.inject.Singleton since it is also supported by other injection frameworks like Dagger.
在这里插入图片描述

5. Instance Bindings

  • 到目前为止,我们都是将type绑定到具体的implemention(实现)。

  • 在某些场景下,我们需要将type绑定到该type的具体实例

  • 例如,在整个应用中jdbc串是固定的,我们可以通过Guice为String类型的jdbcUrl传入固定值

  • MyDatabase的构造函数中,使用@Named("mysql jdbc url")标记MySQL JDBC串,然后通过instance binding为其赋值"jdbc:mysql://localhost/user"

    @Inject
    public MyDatabase(@Named("database")TransactionLog log, @Named("mysql jdbc url")String jdbcUrl) {this.log = log;System.out.println("Initialize MyDatabase with jdbcUrl: " + jdbcUrl);
    }// 通过instance binding配置jdbc url
    bind(String.class).annotatedWith(Names.named("mysql jdbc url")).toInstance("jdbc:mysql://localhost/users");
    
  • 针对基本数据类型、常见类型(如String、enum、Class),还可以使用bindConstant()直接绑定具体实例

    // 创建binding Annotation
    @Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER})
    @Retention(RetentionPolicy.RUNTIME)
    @Qualifier
    public @interface ForDatabase {
    }// 使用binding Annotation
    @Inject
    public MyDatabase(@Named("database") TransactionLog log, @ForDatabase String jdbcUrl) {this.log = log;System.out.println("Initialize MyDatabase with jdbcUrl: " + jdbcUrl);
    }// instance binding,注入具体的String对象
    bindConstant().annotatedWith(ForDatabase.class).to("jdbc:mysql://localhost/users");
    
  • 最终,创建MyDatabase时将打印传入的jdbcUrl

6. Provide Bindings

6.1 @Provides Methods

  • 最简单的方法,使用@Provides method实现对象的创建
  • @Provides method也不陌生了,之前在很多地方都有使用
    • 可以直接定义@Provides method
      @Provides
      public TransactionLog provideTransactionLog() {DatabaseTransactionLog transactionLog = new DatabaseTransactionLog();transactionLog.setJdbcUrl("jdbc:mysql://localhost/pizza");transactionLog.setThreadPoolSize(30);return transactionLog;
      }
      
    • 还可以定义带注解的@Provides method
      // instance binding,定义jdbcUrl的值
      bind(String.class).annotatedWith(Names.named("mysql jdbc url")).toInstance("jdbc:mysql://localhost/users");// Guice自动传入jdbcUrl
      @Provides
      @Database
      public TransactionLog provideTransactionLog(@Named("mysql jdbc url") String jdbcUrl) {DatabaseTransactionLog transactionLog = new DatabaseTransactionLog();transactionLog.setJdbcUrl(jdbcUrl);transactionLog.setThreadPoolSize(30);return transactionLog;
      }// 匹配到使用@Database标识的@Provides method
      @Inject
      public MyDatabase(@Database TransactionLog log) { this.log = log;
      }@Override
      public void log(String msg) {System.out.printf("jdbcUrl: %s, threadPoolSize: %d\n", jdbcUrl, threadPoolSize);
      }
      
    • 最终,为MyDatabase注入的TransactionLog信息为:jdbcUrl: jdbc:mysql://localhost/users, threadPoolSize: 30

6.2 Provider Bindings

  • @Provides method中,创建对象的代码可能变得越来越复杂,也可能由于Module中存在多个@Provides method,导致Module变得越来越臃肿

  • 这时,可以考虑将@Provides method中代码转移到一个独立的类中,这个类实现了Guice的Provider接口

    public interface Provider<T> extends javax.inject.Provider<T> {T get();
    }
    
  • 下面的代码,在Provider的实现类中定义DatabaseTransactionLog的创建逻辑。其中,Connection需要Guice自动注入

    public class DatabaseTransactionLogProvider implements Provider<TransactionLog> {private final Connection connection;@Injectpublic DatabaseTransactionLogProvider(Connection connection) {this.connection = connection;}public TransactionLog get() {DatabaseTransactionLog transactionLog = new DatabaseTransactionLog();transactionLog.setConnection(connection);return transactionLog;}
    }
    
  • 在Module中使用toProvider()定义binding

    bind(TransactionLog.class).toProvider(DatabaseTransactionLogProvider.class);
    

7. Constructor Bindings

  • 之前,我们通过@Inject标识某个构造函数,告诉Guice这既是依赖注入的入口,又是创建实例对象的入口
  • 但是以下情况,@Inject将变得不再适用:
    • 使用的是第三方类
    • 类的多个构造函数参与了依赖注入

如何理解这个类的多个构造函数参与了依赖注入

  • 不同的dependent class,需要注入使用不同的构造函数创建的依赖
  • 例如,有的dependent class只需要注入一个通过默认构造函数创建的依赖,有的dependent class需要注入一个通过有参构造函数创建的依赖

  • 面对上述情况,可以通过Provide binding自己决定使用哪个构造函数创建对象

  • 但是,在AOP这种不能手动构造对象的情况,Provide binding也变得不再适用

  • 这时,可以考虑使用toConstructor()定义constructor binding

  • 定义有多个构造函数的MyTransactionLog

    public class MyTransactionLog implements TransactionLog {private String jdbcUrl;private int threadPoolSize;public MyTransactionLog() {}public MyTransactionLog(@Named("mysql") String jdbcUrl) {this.jdbcUrl = jdbcUrl;}@Overridepublic void log(String msg) {// 打印自身信息System.out.printf("jdbcUrl: %s, threadPoolSize: %d\n", jdbcUrl, threadPoolSize);}
    }
    
  • 向MyDatabase和MyWarehouse注入TransactionLog

    @Inject
    public MyDatabase(@Database TransactionLog log) { this.log = log;
    }@Inject
    public MyWarehouse(@Named("warehouse") TransactionLog log) {this.log = log;
    }
    
  • 使用toConstructor()定义constructor binding,使得MyDatabase和MyWarehouse注入的

    try {// MyDatabase使用的构造函数bind(TransactionLog.class).annotatedWith(Database.class).toConstructor(MyTransactionLog.class.getConstructor(String.class));// MyWarehouse使用的构造函数bind(TransactionLog.class).annotatedWith(Names.named("warehouse")).toConstructor(MyTransactionLog.class.getConstructor());
    } catch (NoSuchMethodException e) {throw new RuntimeException(e);
    }
    
  • 最终,MyDatabase和MyWarehouse将被注入具有不同属性的TransactionLog
    在这里插入图片描述

8. Just-in-time Bindings

  • 以上binding,最终都落在了继承AbstractModule自定义Module中,被叫做显式绑定(explicit binding
  • 如果一个type被需要,却又没有显示绑定,这是Guice会去寻找隐式binding( implicit binding
  • 隐式绑定,又叫Just-In-Time binding,简称 JIT binding

8.1 @Inject的隐式绑定

  • Guice创建一个类的对象时,需要找到该类可以依赖注入的构造函数(injectable constructor

以下两种情况,Guice认为构造函数是可注入的:

  1. 使用@Inject标识的构造函数(推荐的方式)
  2. 无参构造函数:
    • 且定义在非private的类中的、非private构造函数、(实际上,Guice支持private类中private构造函数,但是不推荐,因为反射会导致程序运行变慢)
    • 且未要求必须使用显式绑定
      binder().requireAtInjectRequired();
      
      在这里插入图片描述

以下情况,Guice认为构造函数不是可注入的

  • 未使用@Inject标识的、有一个或多个参数的构造函数
  • 类中不止存在一个使用@Inject标识的构造函数
  • 非静态的内部类中的构造函数

8.2 其他隐式绑定

8.2.1 @ImplementedBy

  • 使用@ImplementedBy定义linked binding

    @ImplementedBy(DatabaseTransactionLog.class)
    public interface TransactionLog {void log(String msg);
    }
    
  • 等价于下面的bind()语句

    bind(TransactionLog.class).to(DatabaseTransactionLog.class);
    
  • 如果既使用了bind(),又使用了@ImplementedBy,则bind()的优先级更高,会覆盖@ImplementedBy的定义

8.2.2 @ProvidedBy

  • 定义好的Provider类,可以通过@ProvidedBy告知Guice

    @ProvidedBy(DatabaseTransactionLogProvider.class)
    public interface TransactionLog {void log(String msg);
    }
    
  • 等价于下面的语句

    bind(TransactionLog.class).toProvider(DatabaseTransactionLogProvider.class);
    

8.3 关闭隐式绑定

  • 从Guice 3.0开始,在configure()方法中使用如下语句,可以关闭隐式绑定

    binder().requireExplicitBindings();
    

9. 后记

  • Guice还提供了很多其他的binding,例如支持binding多个实现的multi binding

    Multibinder<UriSummarizer> uriBinder = Multibinder.newSetBinder(binder(), UriSummarizer.class);
    // 可以Set的形式注入
    Set<UriSummarizer> summarizersMapBinder<String, EncoderFactory> encoderFactories = MapBinder.newMapBinder(binder, String.class, EncoderFactory.class);
    // 可以Map的形式注入
    Map<String, EncoderFactory>
    
  • 需要的小伙伴可以继续深入学习,这里就不再记录总结了

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

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Ncvicat 打开sql文件方法

Nacicat打开sql文件时&#xff0c;有比较多的文章介绍可以直接打开&#xff0c;方法介绍的比较多&#xff0c;但是我遇到了一个坑&#xff0c;就是如何配置环境都无法打开。 本机环境&#xff1a; windows10 mysql 5.7.40 Navicat12.1 一、遇到问题情况 1.1、通过navicat…

【python量化】大幅提升预测性能,将NSTransformer用于股价预测

写在前面 NSTransformer模型来自NIPS 2022的一篇paper《Non-stationary Transformers: Exploring the Stationarity in Time Series Forecasting》。NSTransformer的目的主要是为了解决其他方法出现过平稳化处理的问题。其通过提出序列平稳化以及去平稳化注意力机制可以使得模型…

2023年三月份图形化二级打卡试题

活动时间 从2023年3月1日至3月21日&#xff0c;每天一道编程题。 本次打卡的规则如下&#xff1a; 小朋友每天利用10~15分钟做一道编程题&#xff0c;遇到问题就来群内讨论&#xff0c;我来给大家答疑。 小朋友做完题目后&#xff0c;截图到朋友圈打卡并把打卡的截图发到活动群…

【尚硅谷MySQL入门到高级-宋红康】数据库概述

1、为什么要使用数据库 数据的持久化 2、数据库与数据库管理系统 2.1 数据库的相关概念 2.2 数据库与数据库管理系统的关系 3、 MySQL介绍 MySQL从5.7版本直接跳跃发布了8.0版本 &#xff0c;可见这是一个令人兴奋的里程碑版本。MySQL 8版本在功能上做了显著的改进与增强&a…

CXL技术分析

CXL&#xff0c;全称Compute Express Link&#xff0c;该技术由Intel牵头开发用于高性能计算、数据中心&#xff0c;主要解决处理器、加速器和内存之间的cache一致性问题&#xff0c;可消除CPU、专用加速器的计算密集型工作负载的传输瓶颈&#xff0c;显著提升系统性能。 一、…

python的装饰器与设计模式中的装饰器模式

相信很多人在初次接触python中的装饰器时&#xff0c;会跟我一样有个疑问&#xff0c;这跟设计模式中的装饰器模式有什么区别吗&#xff1f;本质上是一样的&#xff0c;都是对现有对象&#xff0c;包括函数或者类的一种扩展。这篇文档将进行对比分析。 python的装饰器 装饰器…

duboo+zookeeper分布式架构入门

分布式 dubbo Zookeeper 分布式系统就是若干独立计算机的集合&#xff08;并且这些计算机之间相互有关联&#xff0c;就像是一台计算机中的C盘F盘等&#xff09;&#xff0c;这些计算对于用户来说就是一个独立的系统。 zookeeper安装 下载地址&#xff1a;Index of /dist/z…

【数据库系统概论】基础知识总结

&#x1f339;作者:云小逸 &#x1f4dd;个人主页:云小逸的主页 &#x1f4dd;Github:云小逸的Github &#x1f91f;motto:要敢于一个人默默的面对自己&#xff0c;强大自己才是核心。不要等到什么都没有了&#xff0c;才下定决心去做。种一颗树&#xff0c;最好的时间是十年前…

C++10:非类型模板参数以及模板的特化

目录 非类型模板参数 模板的特化 模板类的特化 1.全特化 2.偏特化 模板其实还有其他的玩法&#xff0c;比如非类型模板参数以及模板的特化。 非类型模板参数 在记述非类型模板参数前&#xff0c;我们认识一下C中一个比较鸡肋的类&#xff0c;array #include<iostream&g…

k8s-yaml文件

文章目录一、K8S支持的文件格式1、yaml和json的主要区别2、YAML语言格式二、YAML1、查看 API 资源版本标签2、编写资源配置清单2.1 编写 nginx-test.yaml 资源配置清单2.2 创建资源对象2.3 查看创建的pod资源3、创建service服务对外提供访问并测试3.1 编写nginx-svc-test.yaml文…

数据仓库Hive

HIve介绍 Hive是建立在Hadoop上的数据仓库基础构架。它提供了一系列的工具&#xff0c;可以用来进行数据提取转化加载&#xff0c;可以简称为ETL。 Hive 定义了简单的类SQL查询语言&#xff0c;称为HQL&#xff0c;它允许熟悉SQL的用户直接查询Hadoop中的数据&#xf…

如何从0创建Spring Cloud Alibaba(多模块)

以一个父工程带两个Module&#xff08;test1、test2&#xff09;为例。 一、创建父工程 由于是模块化项目&#xff0c;那么父工程不需要实际的代码逻辑&#xff0c;因此无需创建src&#xff0c;那么可以有几种方式创建&#xff0c;例如&#xff1a; 使用Spring Initializr脚…

腾讯一面—Android 系统启动流程详解

正文AMS 是 Android 中最核心的服务之一&#xff0c;主要负责系统中四大组件的启动、切换、调度及应用进程的管理和调度等工作&#xff0c;其职责与操作系统中的进程管理和调度模块相类似&#xff0c;它本身也是一个 Binder 的实现类&#xff0c;应用进程能通过 Binder 机制调用…

ARM Context synchronization event和Instruction Synchronization Barrier

在Arm architecture里&#xff0c;经常提到Context synchronization event(CSE)和Explicit synchronization&#xff0c;Context synchronization events在之前是叫作context synchronization operations。Explicit synchronization是Context synchronization event的结果&…

基于yolov5与改进VGGNet的车辆多标签实时识别算法

摘 要 为了能快速、有效地识别视频中的车辆信息&#xff0c;文中结合YOLOv3算法和CNN算法的优点&#xff0c;设计了一种能实时识别车辆多标签信息的算法。首先&#xff0c;利用具有较高识别速度和准确率的YOLOv3实现对视频流中车辆的实时监测和定位。在获得车辆的位置信息后…

如何提高机器人专业课讲师的收入

先放一些总结&#xff1a;为什么我是不合格的高校机器人工程专业讲师&#xff1f;2020不合格肯定收入不会提升&#xff0c;甚至失业风险会非常高的。为何所做的课程努力几乎全部失败呢&#xff1f;→机器人工程类← 2022不能一次次失败&#xff0c;因为只有自己会为失败买单&am…

CUDA 内存系统

CUDA 内存系统 本文主要是针对<cuda c编程权威指南>的总结,由于原书出版的时候cuda刚刚出到cuda6,之后的cuda版本可能有更新,可能需要我翻一翻文档,待更新. 内存系统架构图 常见的内存作用域与生存期 新特性 早期的 Kepler 架构中一个颇为好用的特性就是 CUDA 程序员可…

JVM - G1垃圾收集器深入剖析

​​​​​​​1、G1收集器概述 HotSpot团队一直努力朝着高效收集、减少停顿(STW: Stop The World)的方向努力&#xff0c;也贡献了从串行Serial收集器、到并行收集器Parallerl收集器&#xff0c;再到CMS并发收集器&#xff0c;乃至如今的G1在内的一系列优秀的垃圾收集器。 G…

Spring Cache的基本使用与分析

概述 使用 Spring Cache 可以极大的简化我们对数据的缓存&#xff0c;并且它封装了多种缓存&#xff0c;本文基于 redis 来说明。 基本使用 1、所需依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-…

随想录二刷Day06——链表

文章目录链表6. 删除链表的倒数第 N 个结点7. 链表相交8. 环形链表 II链表 6. 删除链表的倒数第 N 个结点 19. 删除链表的倒数第 N 个结点 思路&#xff1a; 用双指针的方法&#xff0c;fast 和 slow 之间保持距离为 n&#xff0c;只需要遍历一次即可完成删除任务。 为了方便…