google guice是一款简洁的依赖注入框架,如果你想要依赖注入功能,以及AOP功能,又不想引入庞大的Spring Framework,可以考虑使用guice.
下面我会简单翻译github上guice的wiki。
考虑一个支付服务:
public interface PayService {
boolean pay(long userId, double money);
}
一个简单实现如下:
public class RealPayService implements PayService {
@Override
public boolean pay(long userId, double money) {
//编译期硬编码,难以测试
PayProcessor payProcessor = new BankPayProcessor();
TransactionLog log = new DbTransactionLog();
try {
//支付
payProcessor.doPay(userId, money);
//打日志
log.log("user[" + userId + "]支付了[" + money + "]");
return true;
} catch (Exception e) {
log.log("系统错误:" + e.getMessage());
return false;
}
}
}
由于BankPayProcessor和DbTransactionLog以局部变量形式创建,使得代码难以测试(无法使用Mock实现替换)。
使用依赖注入:
public class RealPayService implements PayService {
private PayProcessor payProcessor;
private TransactionLog log;
//从外部注入依赖
public RealPayService(PayProcessor payProcessor, TransactionLog log) {
super();
this.payProcessor = payProcessor;
this.log = log;
}
@Override
public boolean pay(long userId, double money) {
try {
//支付
payProcessor.doPay(userId, money);
//打日志
log.log("user[" + userId + "]支付了[" + money + "]");
return true;
} catch (Exception e) {
log.log("系统错误:" + e.getMessage());
return false;
}
}
}
于是单元测试可编写如下:
public class RealPayServiceTest {
@Test
public void testPay() throws Exception {
//创建依赖对象
PayProcessor payProcessor = new MockPayProcessor();
TransactionLog log = new MockTransactionLog();
//注入
RealPayService payService = new RealPayService(payProcessor, log);
assertTrue(payService.pay(1L, 100.0));
}
}
解决了代码可测试性,模块化的问题,但创建依赖对象,以及执行注入的代码非常繁琐枯燥。
public class PayModule extends AbstractModule {
@Override
protected void configure() {
//配置接口对应的实现类,依赖注入时将实例化对应的实现类
bind(PayService.class).to(RealPayService.class);
bind(PayProcessor.class).to(BankPayProcessor.class);
bind(TransactionLog.class).to(DbTransactionLog.class);
}
}
//从外部注入依赖
@Inject
public RealPayService(PayProcessor payProcessor, TransactionLog log) {
super();
this.payProcessor = payProcessor;
this.log = log;
}
public static void main(String[] args) {
//应用生命周期通常只调用一次
Injector payInjector = Guice.createInjector(new PayModule());
//获取实例,依赖注入由Guice完成
PayService payService = payInjector.getInstance(PayService.class);
payService.pay(1L, 100.0);
}
guice可以定义匹配规则,匹配要拦截的方法,实现AOP编程。
接续上面的例子,假设周四无法支付(今天刚好周四)。
/**
* 标注到要拦截的方法上
* @author ljf
*
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface NotOnThursday {
}
public class RealPayService implements PayService {
@NotOnThursday
@Override
public boolean pay(long userId, double money) {
}
}
public class ThursdayBlocker implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
Calendar calendar = Calendar.getInstance();
if (calendar.get(Calendar.DAY_OF_WEEK) == Calendar.THURSDAY) {
throw new IllegalStateException("周四无法调用" + invocation.getMethod().getName());
}
return invocation.proceed();
}
}
public class PayModule extends AbstractModule {
@Override
protected void configure() {
//配置对pay方法的拦截规则
bindInterceptor(Matchers.any(), Matchers.annotatedWith(NotOnThursday.class), new ThursdayBlocker());
}
}
5.运行效果
Exception in thread "main" java.lang.IllegalStateException: 周四无法调用pay
at otaku.dogdog.ThursdayBlocker.invoke(ThursdayBlocker.java:14)
移步 guice wiki