轻量级依赖注入框架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>
具体的发行版参考下述版本。
三、快速开始
我们先想象有一个简单的订单支付的业务。有支付服务,订单服务,价格计算服务。
建立价格服务接口和其对应的实现
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的绑定。