1. 简介

本文将深入探讨 Google Guice 的核心概念,并演示如何使用 Guice 完成基础的依赖注入(DI)任务。我们还会对比 Guice 与成熟 DI 框架(如 Spring 和 CDI)的实现差异。

⚠️ 本文假设读者已掌握依赖注入模式的基础知识。

2. 环境配置

在 Maven 项目中使用 Google Guice,需在 pom.xml 中添加以下依赖:

<dependency>
    <groupId>com.google.inject</groupId>
    <artifactId>guice</artifactId>
    <version>7.0.0</version>
</dependency>

此外,Guice 还提供扩展包第三方模块,用于增强框架能力(主要是集成主流 Java 框架)。

3. Guice 基础依赖注入

3.1 示例应用

我们设计一个支持客服系统三种通信方式的场景:邮件、短信和即时消息。先看核心类:

public class Communication {
 
    @Inject 
    private Logger logger;
    
    @Inject
    private Communicator communicator;

    public Communication(Boolean keepRecords) {
        if (keepRecords) {
            System.out.println("Message logging enabled");
        }
    }
 
    public boolean sendMessage(String message) {
        return communicator.sendMessage(message);
    }
}

Communication 是通信基础单元,通过 Communicator 实现消息传输。Guice 的入口是 Injector

public static void main(String[] args){
    Injector injector = Guice.createInjector(new BasicModule());
    Communication comms = injector.getInstance(Communication.class);
}

这里引入了 Guice 的核心概念:Module(示例中的 BasicModule)。Module 是绑定的基本定义单元(类似 Spring 的 wiring)。

✅ Guice 采用代码优先的依赖注入方式,开箱即用无需大量 XML 配置。
✅ 只要类有无参构造函数,Guice 会通过即时绑定(just-in-time binding) 隐式注入依赖树(Guice 创世即有此特性,Spring 4.3 才支持)。

3.2 基础绑定

绑定(Binding)是 Guice 的核心机制,用于定义依赖注入规则。绑定在 AbstractModule 实现类中定义:

public class BasicModule extends AbstractModule {
 
    @Override
    protected void configure() {
        bind(Communicator.class).to(DefaultCommunicatorImpl.class);
    }
}

此模块指定:所有 Communicator 变量都将注入 DefaultCommunicatorImpl 实例。

3.3 命名绑定

命名绑定(Named Binding)是绑定的变种,看以下变量声明:

@Inject @Named("DefaultCommunicator")
Communicator communicator;

对应的绑定定义:

@Override
protected void configure() {
    bind(Communicator.class)
      .annotatedWith(Names.named("DefaultCommunicator"))
      .to(DefaultCommunicatorImpl.class);
}

此绑定将为 @Named("DefaultCommunicator") 注解的变量提供 Communicator 实例。

⚠️ @Inject@Named 注解虽借自 Jakarta EE 的 CDI,但实际位于 com.google.inject.* 包。使用 IDE 时需注意导入正确包。
提示:Guice 也支持 javax.inject.Injectjavax.inject.Named 等标准注解。

3.4 构造函数绑定

通过构造函数绑定(Constructor Binding)可注入无默认构造函数的依赖

public class BasicModule extends AbstractModule {
 
    @Override
    protected void configure() {
        bind(Boolean.class).toInstance(true);
        bind(Communication.class).toConstructor(
          Communication.class.getConstructor(Boolean.TYPE));
}

上述代码通过 boolean 参数的构造函数注入 Communication 实例。我们通过无目标绑定(untargeted binding) 为构造函数提供 true 参数。

此外,实例绑定(Instance Binding) 是另一种构造函数绑定方式:

public class BasicModule extends AbstractModule {
 
    @Override
    protected void configure() {
        bind(Communication.class)
          .toInstance(new Communication(true));
    }    
}

此绑定直接提供 Communication 实例。但需注意: ❌ 类的依赖树不会自动装配
❌ 仅适用于无复杂初始化或依赖注入的场景

4. 依赖注入类型

Guice 支持标准 DI 模式下的所有注入类型。在 Communicator 类中,我们需要注入不同类型的 CommunicationMode

4.1 字段注入

@Inject @Named("SMSComms")
CommunicationMode smsComms;

可选的 @Named 注解作为限定符,实现基于名称的定向注入。

4.2 方法注入

使用 setter 方法实现注入:

@Inject
public void setEmailCommunicator(@Named("EmailComms") CommunicationMode emailComms) {
    this.emailComms = emailComms;
}

4.3 构造函数注入

通过构造函数注入依赖:

@Inject
public Communication(@Named("IMComms") CommunicationMode imComms) {
    this.imComms= imComms;
}

4.4 隐式注入

Guice 会隐式注入通用组件,如 Injectorjava.util.Logger。示例中虽使用 logger,但无需显式绑定。

5. Guice 作用域

Guice 支持主流 DI 框架的作用域机制,默认每次注入都创建新实例。

5.1 单例

注入单例:

bind(Communicator.class).annotatedWith(Names.named("AnotherCommunicator"))
  .to(Communicator.class).in(Scopes.SINGLETON);

in(Scopes.SINGLETON) 指定所有 @Named("AnotherCommunicator") 注解的 Communicator 字段将获得单例实例(默认懒加载)。

5.2 饿汉单例

注入饿汉单例:

bind(Communicator.class).annotatedWith(Names.named("AnotherCommunicator"))
  .to(Communicator.class)
  .asEagerSingleton();

asEagerSingleton() 定义单例为饿汉式实例化。

除这两种作用域外,Guice 还支持自定义作用域,以及 Jakarta EE 提供的 Web 专用注解 @RequestScoped@SessionScoped(Guice 未提供这些注解的替代版本)。

6. Guice 面向切面编程

Guice 遵循 AOPAlliance 规范实现 AOP。我们通过四步实现日志拦截器,用于跟踪消息发送:

步骤 1 – 实现 AOPAlliance 的 MethodInterceptor

public class MessageLogger implements MethodInterceptor {

    @Inject
    Logger logger;

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        Object[] objectArray = invocation.getArguments();
        for (Object object : objectArray) {
            logger.info("Sending message: " + object.toString());
        }
        return invocation.proceed();
    }
}

步骤 2 – 定义普通 Java 注解

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MessageSentLoggable {
}

步骤 3 – 定义匹配器绑定

匹配器(Matcher)指定 AOP 注解的应用目标。此处限定注解应用于 CommunicationMode 的实现类:

public class AOPModule extends AbstractModule {

    @Override
    protected void configure() {
        bindInterceptor(
            Matchers.any(),
            Matchers.annotatedWith(MessageSentLoggable.class),
            new MessageLogger()
        );
    }
}

此匹配器将 MessageLogger 拦截器应用于所有方法带 MessageSentLoggable 注解的类。

步骤 4 – 应用注解并加载模块

@Override
@MessageSentLoggable
public boolean sendMessage(String message) {
    logger.info("SMS message sent");
    return true;
}

public static void main(String[] args) {
    Injector injector = Guice.createInjector(new BasicModule(), new AOPModule());
    Communication comms = injector.getInstance(Communication.class);
}

7. 总结

通过分析 Guice 基础功能,可见其设计灵感源于 Spring。Guice 支持 JSR-330,定位为专注注入的 DI 框架(而 Spring 提供完整编程生态,不仅是 DI),面向需要 DI 灵活性的开发者。

Guice 高度可扩展,允许开发者编写可移植插件,实现框架的灵活创新应用。此外,Guice 已深度集成主流框架和平台,如 Servlets、JSF、JPA 和 OSGi 等。


原始标题:Guide to Google Guice | Baeldung