轻量级依赖注入框架Google Guice(1)DI

轻量级依赖注入框架Google Guice(1)DI

一、Google Guice简介

Google Guice (读作”juice”)是超轻量级的,下一代的,为Java 5及后续版本设计的依赖注入容器。

它在连接对象、访问中间层等方面,体现了最大程度的灵活性和可维护性。

​ 正所谓谷歌出品,必属精品,况且Guice还出自于它的广告这种多金部门,自然也不例外。Google Guice被大量应用于谷歌内部,然后2010年开源出来。虽然业界反响并不大,但是因为它的轻量级,有些流行的开源框架(如Druid、Apollo、Elastic Search、Play2)把它作为基础的DI组件。

二、基础配置

导入maven依赖。

        <dependency>
            <groupId>com.google.inject</groupId>
            <artifactId>guice</artifactId>
            <version>${guice.version}</version>
        </dependency>
        <dependency>
            <groupId>com.google.inject.extensions</groupId>
            <artifactId>guice-multibindings</artifactId>
            <version>${guice.version}</version>
        </dependency>

具体的发行版参考下述版本。

guice版本

三、快速开始

我们先想象有一个简单的订单支付的业务。有支付服务,订单服务,价格计算服务。

建立价格服务接口和其对应的实现

public interface PriceService {

    /**
     * <p>获取支持的货币</p>
     *
     * @description: 获取支持的货币
     * @return java.util.List<java.lang.String>
     * @author: junjie he
     * @create: 2022/8/2
     */
    List<String> getSupportedCurrencies();

    /**
     * <p>获取价格</p>
     *
     * @description: 获取价格
     * @param orderId 订单号
     * @return long
     * @author: junjie he
     * @create: 2022/8/2
     */
    long getPrice(long orderId);

}

建立订单服务接口和其对应的实现

public interface OrderService {

    /**
     * <p>发送订单</p>
     *
     * @description: 发送订单
     * @param orderId 订单号
     * @return void
     * @author: junjie he
     * @create: 2022/8/2
     */
    void sendToPayment(long orderId);

}

建立支付服务接口和其对应的实现

public interface PaymentService {

    /**
     * <p>付款操作</p>
     *
     * @description: 付款操作
     * @param orderId 订单号
     * @param  price 价格
     * @param  sessionId 会话ID
     * @return void
     * @author: junjie he
     * @create: 2022/8/2
     */
    void pay(long orderId, long price, Object sessionId);

}

建立一个服务Module,将服务和模块进行关联。 在Guice中需要定义Module来进行关联,注入的配置是自写的Java类,必须继承AbstractModule抽象类,重写configure方法。

public class ServerModule extends AbstractModule {

    @Override
    protected void configure() {
        //相当于将实现类注入
        bind(OrderService.class).to(OrderServerImpl.class);
        bind(PaymentService.class).to(PaymentServiceImpl.class);
        bind(PriceService.class).to(PriceServiceImpl.class);
     }
}

再编写一下相关的测试类,提供程序的启动入口。

public class OrderServerTest {

    @Inject
    private OrderService orderService;

    @Inject
    private PriceService priceService;

    @Before
    public void SetUp() {
        //利用injectMembers,将当前所需的类具现化
        Guice.createInjector(new ServerModule()).injectMembers(this);
    }

    @Test
    public void testSendToPayment() {
        orderService.sendToPayment(789L);
    }

    @Test
    public void testSupportedCurrencies() {
        throw new RuntimeException(
            priceService.getSupportedCurrencies().toString()
        );
    }

}

大功告成,接下来介绍我们的几种注入方式。

四、常见的几种注入方式

注入流程图解

Guice注入图解如上,通过@Inject进行注入,这点跟spring还是有点类似,可以用构造方式注入,也可以直接注入到每个成员变量上,以下将列举集中注入方式。

1. 纯类型注入

price模块中添加一个price的成员变量,然后在其构造中加入@Inject注解,同时在ServerModule中加入bind来绑定该类型值。完整代码如下:

    // PriceServiceImpl中添加成员变量
    private final Long price;
    // 构造做相应的改造
    @Inject
    public PriceServiceImpl(@Named("getSupported") Provider<List<String>> supportedCurrencies, 
    Long price) {
        this.supportedCurrencies = supportedCurrencies;
        this.price = price;
    }


    // ServerModule中添加绑定
    bind(Long.class).toInstance(1234L);

    // 运行结果
    java.lang.RuntimeException: Price=1234.SessionId=1659439200620.orderPaid=1

可见price已经被绑定成了设置的1234。

2. Provider注入

将上述price的成员变量,改为Provider接口提供的值。同时在ServerModule中添加相关的Provider即可完成注入,完整代码如下:

    // PriceServiceImpl中修改成员变量为Provider
    private final Provider<Long> price;

    // ServerModule中绑定为provider并用函数式进行简化
    bind(Long.class).toProvider(() -> 1234L);

    // 或者写一个 @Provides 的函数,类似于spring的bean
    @Provides
    Long generatePrice() {
        return 1234L;
    }

可见,使用Provider注入会变得相当的灵活。

3. 命名注入

很多时候单一注入的方式并不能满足我们的需求,这时可以使用命名注入的方式,有点指哪打哪的意思,注入的时候只会关注名字与类型的匹配,这样可读性更高,并且更灵活。

相关细节以及完整代码,如下

    // 构造函数中加入 @Named("price") 来标记该字段
    @Inject
    public PriceServiceImpl(@Named("getSupported") Provider<List<String>> supportedCurrencies,     @Named("price") Long price) {
        this.supportedCurrencies = supportedCurrencies;
        this.price = price;
    }

同时我们也可以通过采用方法注入( Method Injection) 或者 场注入( Field Injection) 这两种方式来完成我们的注入,这点和Spring的自动装载也比较类似。

相关细节以及完整代码,如下

    // 下述为 方法注入( Method Injection) 方式
    @Inject
    public void setSupportedCurrencies(@Named("getSupported") Provider<List<String>>                 supportedCurrencies) {
        this.supportedCurrencies = supportedCurrencies;
    }

    @Inject
    public void setPrice(@Named("price") Long price) {
        this.price = price;
    }

    // 下述为场注入( Field Injection) 方式
    @Inject
    @Named("price")
    private Long price;

注入写好了,在绑定操作的时候就必须绑定到相关命名注解才可以。

// 绑定的时候绑定到命名的注解
bind(Long.class).annotatedWith(Names.named("price")).toProvider(() -> 1234L);

当然可以自定义注解,来代替@Named进行使用,因为使用方法大致上是一样的,就不再进行赘述。

未完待续… 后面专题介绍Guice的绑定。


   转载规则


《轻量级依赖注入框架Google Guice(1)DI》 Malroy 采用 知识共享署名 4.0 国际许可协议 进行许可。
 上一篇
轻量级依赖注入框架Google Guice(2)BIND 轻量级依赖注入框架Google Guice(2)BIND
轻量级依赖注入框架Google Guice(2)BIND一、前言​ 前面介绍了Google Guice的注入,跟它相辅相成的就是绑定。绑定的话,可以说是相当的重要,因为就是先有绑定这一个操作,然后才能进行注入。详细的绑定图解如下:
2022-08-03
下一篇 
浅谈控制反转与依赖注入(转载) 浅谈控制反转与依赖注入(转载)
浅谈控制反转与依赖注入第一章:小明和他的手机从前有个人叫小明 小明有三大爱好,抽烟,喝酒…… 咳咳,不好意思,走错片场了。应该是逛知乎、玩王者农药和抢微信红包 小明的三大爱好 我们用一段简单的伪代码,来制造一个这样的小明 clas
2022-07-29
  目录