点击上方 "程序员小乐"关注, 星标或置顶一起成长
每天凌晨00点00分, 第一时间与你相约
每日英文
Be true to who you are. Stop trying to please other people or be someone else. It’s better to be an original version of yourself than an exact duplicate of someone else.
做真实的自己,不要为了取悦别人或试图成为某个人。做你最原始的自己,比做任何人的复制品都来得好。
每日掏心话
大多数人的压力,来自于不假思索跟着大部队走。大部队说,要先有钱才能有面子,于是终日焦虑财富不够,忘了品味生活。
来自:GinoBeFunny | 责编:乐乐
链接:zhuanlan.zhihu.com/p/24924391
程序员小乐(ID:study_tech)第 752 次推文 图片来自 Pexels
往日回顾:重磅!美国对巴西发出警告:敢用华为就降低安全级别,巴西霸气回应!
正文
Elasticsearch大量使用了Guice,本文简单的介绍下Guice的基本概念和使用方式。
概述:了解Guice是什么,有什么特点;
快速开始:通过实例了解Guice;
核心概念:了解Guice涉及的核心概念,如绑定(Binding)、范围(Scope)和注入(Injection);
Guice概述
Guice是Google开源的依赖注入类库,通过Guice减少了对工厂方法和new的使用,使得代码更易交付、测试和重用
Guice可以帮助我们更好地设计API,它是个轻量级非侵入式的类库;
Guice对开发友好,当有异常发生时能提供更多有用的信息用于分析;
public interface BillingService { /** * 通过信用卡支付。无论支付成功与否都需要记录交易信息。 * * @return 交易回执。支付成功时返回成功信息,否则记录失败原因。 */ Receipt chargeOrder(PizzaOrder order, CreditCard creditCard);}
使用new的方式获取信用卡支付处理器和数据库交易日志记录器:
public class RealBillingService implements BillingService { public Receipt chargeOrder(PizzaOrder order, CreditCard creditCard) { CreditCardProcessor processor = new PaypalCreditCardProcessor(); TransactionLog transactionLog = new DatabaseTransactionLog(); try { ChargeResult result = processor.charge(creditCard, order.getAmount()); transactionLog.logChargeResult(result); return result.wasSuccessful() ? Receipt.forSuccessfulCharge(order.getAmount()) : Receipt.forDeclinedCharge(result.getDeclineMessage()); } catch (UnreachableException e) { transactionLog.logConnectException(e); return Receipt.forSystemFailure(e.getMessage()); } }}
public class RealBillingService implements BillingService { private final CreditCardProcessor processor; private final TransactionLog transactionLog; public RealBillingService(CreditCardProcessor processor, TransactionLog transactionLog) { this.processor = processor; this.transactionLog = transactionLog; } public Receipt chargeOrder(PizzaOrder order, CreditCard creditCard) { try { ChargeResult result = processor.charge(creditCard, order.getAmount()); transactionLog.logChargeResult(result); return result.wasSuccessful() ? Receipt.forSuccessfulCharge(order.getAmount()) : Receipt.forDeclinedCharge(result.getDeclineMessage()); } catch (UnreachableException e) { transactionLog.logConnectException(e); return Receipt.forSystemFailure(e.getMessage()); } }}
public static void main(String[] args) { CreditCardProcessor processor = new PaypalCreditCardProcessor(); TransactionLog transactionLog = new DatabaseTransactionLog(); BillingService billingService = new RealBillingService(processor, transactionLog); ...}
中可以注入Mock类:
public class RealBillingServiceTest extends TestCase { private final PizzaOrder order = new PizzaOrder(100); private final CreditCard creditCard = new CreditCard("1234", 11, 2010); private final InMemoryTransactionLog transactionLog = new InMemoryTransactionLog(); private final FakeCreditCardProcessor processor = new FakeCreditCardProcessor(); public void testSuccessfulCharge() { RealBillingService billingService = new RealBillingService(processor, transactionLog); Receipt receipt = billingService.chargeOrder(order, creditCard); assertTrue(receipt.hasSuccessfulCharge()); assertEquals(100, receipt.getAmountOfCharge()); assertEquals(creditCard, processor.getCardOfOnlyCharge()); assertEquals(100, processor.getAmountOfOnlyCharge()); assertTrue(transactionLog.wasSuccessLogged()); }}
public class BillingModule extends AbstractModule { @Override protected void configure() { bind(TransactionLog.class).to(DatabaseTransactionLog.class); bind(CreditCardProcessor.class).to(PaypalCreditCardProcessor.class); bind(BillingService.class).to(RealBillingService.class); }}
然后只需在原有的构造方法中增加@Inject注解即可注入:
public class RealBillingService implements BillingService { private final CreditCardProcessor processor; private final TransactionLog transactionLog; @Inject public RealBillingService(CreditCardProcessor processor, TransactionLog transactionLog) { this.processor = processor; this.transactionLog = transactionLog; } public Receipt chargeOrder(PizzaOrder order, CreditCard creditCard) { try { ChargeResult result = processor.charge(creditCard, order.getAmount()); transactionLog.logChargeResult(result); return result.wasSuccessful() ? Receipt.forSuccessfulCharge(order.getAmount()) : Receipt.forDeclinedCharge(result.getDeclineMessage()); } catch (UnreachableException e) { transactionLog.logConnectException(e); return Receipt.forSystemFailure(e.getMessage()); } }}
最后,再看看main方法中是如何调用的:
public static void main(String[] args) { Injector injector = Guice.createInjector(new BillingModule()); BillingService billingService = injector.getInstance(BillingService.class); ...}
绑定
连接绑定
连接绑定是最常用的绑定方式,它将一个类型和它的实现进行映射。下面的例子中将TransactionLog接口映射到它的实现类DatabaseTransactionLog。
public class BillingModule extends AbstractModule { @Override protected void configure() { bind(TransactionLog.class).to(DatabaseTransactionLog.class); }}
public class BillingModule extends AbstractModule { @Override protected void configure() { bind(TransactionLog.class).to(DatabaseTransactionLog.class); bind(DatabaseTransactionLog.class).to(MySqlDatabaseTransactionLog.class); }}
@BindingAnnotation @Target({ FIELD, PARAMETER, METHOD })@Retention(RUNTIME)public @interface PayPal {}public class RealBillingService implements BillingService { @Inject public RealBillingService(@PayPal CreditCardProcessor processor, TransactionLog transactionLog) { ... }}// 当注入的方法参数存在@PayPal注解时注入PayPalCreditCardProcessor实现bind(CreditCardProcessor.class).annotatedWith(PayPal.class).to(PayPalCreditCardProcessor.class);
public class RealBillingService implements BillingService { @Inject public RealBillingService(@Named("Checkout") CreditCardProcessor processor, TransactionLog transactionLog) { ... }}// 当注入的方法参数存在@Named注解且值为Checkout时注入CheckoutCreditCardProcessor实现bind(CreditCardProcessor.class).annotatedWith(Names.named("Checkout")).to(CheckoutCreditCardProcessor.class);
bind(String.class).annotatedWith(Names.named("JDBC URL")).toInstance("jdbc:mysql://localhost/pizza");bind(Integer.class).annotatedWith(Names.named("login timeout seconds")).toInstance(10);
public class BillingModule extends AbstractModule { @Override protected void configure() { ... } @Provides TransactionLog provideTransactionLog() { DatabaseTransactionLog transactionLog = new DatabaseTransactionLog(); transactionLog.setJdbcUrl("jdbc:mysql://localhost/pizza"); transactionLog.setThreadPoolSize(30); return transactionLog; } @Provides @PayPal CreditCardProcessor providePayPalCreditCardProcessor(@Named("PayPal API key") String apiKey) { PayPalCreditCardProcessor processor = new PayPalCreditCardProcessor(); processor.setApiKey(apiKey); return processor; }}
public interface Provider<T> { T get();}public class DatabaseTransactionLogProvider implements Provider<TransactionLog> { private final Connection connection; @Inject public DatabaseTransactionLogProvider(Connection connection) { this.connection = connection; } public TransactionLog get() { DatabaseTransactionLog transactionLog = new DatabaseTransactionLog(); transactionLog.setConnection(connection); return transactionLog; }}public class BillingModule extends AbstractModule { @Override protected void configure() { bind(TransactionLog.class).toProvider(DatabaseTransactionLogProvider.class); }}
bind(MyConcreteClass.class);bind(AnotherConcreteClass.class).in(Singleton.class);
public class BillingModule extends AbstractModule { @Override protected void configure() { try { bind(TransactionLog.class).toConstructor(DatabaseTransactionLog.class.getConstructor(DatabaseConnection.class)); } catch (NoSuchMethodException e) { addError(e); } }}
@Singletonpublic class InMemoryTransactionLog implements TransactionLog { /* everything here should be threadsafe! */}// scopes apply to the binding source, not the binding targetbind(TransactionLog.class).to(InMemoryTransactionLog.class).in(Singleton.class);@Provides @SingletonTransactionLog provideTransactionLog() { ...}
// Eager singletons reveal initialization problems sooner,// and ensure end-users get a consistent, snappy experience.bind(TransactionLog.class).to(InMemoryTransactionLog.class).asEagerSingleton();
注入
// 构造器注入public class RealBillingService implements BillingService { private final CreditCardProcessor processorProvider; private final TransactionLog transactionLogProvider; @Inject public RealBillingService(CreditCardProcessor processorProvider, TransactionLog transactionLogProvider) { this.processorProvider = processorProvider; this.transactionLogProvider = transactionLogProvider; }}// 方法注入public class PayPalCreditCardProcessor implements CreditCardProcessor { private static final String DEFAULT_API_KEY = "development-use-only"; private String apiKey = DEFAULT_API_KEY; @Inject public void setApiKey(@Named("PayPal API key") String apiKey) { this.apiKey = apiKey; }}// 属性注入public class DatabaseTransactionLogProvider implements Provider<TransactionLog> { @Inject Connection connection; public TransactionLog get() { return new DatabaseTransactionLog(connection); }}// 可选注入:当找不到映射时不报错public class PayPalCreditCardProcessor implements CreditCardProcessor { private static final String SANDBOX_API_KEY = "development-use-only"; private String apiKey = SANDBOX_API_KEY; @Inject(optional=true) public void setApiKey(@Named("PayPal API key") String apiKey) { this.apiKey = apiKey; }}
辅助注入
辅助注入(Assisted Inject)属于Guice扩展的一部分,它通过@Assisted注解自动生成工厂来加强非注入参数的使用。
// RealPayment中有两个参数startDate和amount无法直接注入public class RealPayment implements Payment { public RealPayment( CreditService creditService, // from the Injector AuthService authService, // from the Injector Date startDate, // from the instance's creator Money amount); // from the instance's creator } ...}// 一种方式是增加一个工厂来构造public interface PaymentFactory { public Payment create(Date startDate, Money amount);}public class RealPaymentFactory implements PaymentFactory { private final Provider<CreditService> creditServiceProvider; private final Provider<AuthService> authServiceProvider; @Inject public RealPaymentFactory(Provider<CreditService> creditServiceProvider, Provider<AuthService> authServiceProvider) { this.creditServiceProvider = creditServiceProvider; this.authServiceProvider = authServiceProvider; } public Payment create(Date startDate, Money amount) { return new RealPayment(creditServiceProvider.get(), authServiceProvider.get(), startDate, amount); }}bind(PaymentFactory.class).to(RealPaymentFactory.class);// 通过@Assisted注解可以减少RealPaymentFactorypublic class RealPayment implements Payment { @Inject public RealPayment( CreditService creditService, AuthService authService, @Assisted Date startDate, @Assisted Money amount); } ...}// Guice 2.0//bind(PaymentFactory.class).toProvider(FactoryProvider.newFactory(PaymentFactory.class, RealPayment.class));// Guice 3.0install(new FactoryModuleBuilder().implement(Payment.class, RealPayment.class).build(PaymentFactory.class));
最小化可变性:尽可能注入的是不可变对象;
只注入直接依赖:不用注入一个实例来获取真正需要的实例,增加复杂性且不易测试;
避免循环依赖
避免静态状态:静态状态和可测试性就是天敌;
采用@Nullable:Guice默认情况下禁止注入null对象;
模块的处理必须要快并且无副作用
在Providers绑定中当心IO问题:因为Provider不检查异常、不支持超时、不支持重试;
不用在模块中处理分支逻辑
尽可能不要暴露构造器
欢迎在留言区留下你的观点,一起讨论提高。如果今天的文章让你有新的启发,学习能力的提升上有新的认识,欢迎转发分享给更多人。
欢迎各位读者加入程序员小乐技术群,在公众号后台回复“加群”或者“学习”即可。
猜你还想看
阿里、腾讯、百度、华为、京东最新面试题汇集
Java中关于try、catch、finally中的细节分析,看了都说好!
IDEA热部署之JRebel的安装与破解教程
为什么要看源码?如何看源码?高手进阶必看!
文章有问题?点此查看未经处理的缓存