Google Guice使用入门

1. Guice是什么

Guice(当前最新版本是4.1)是Google开发的轻量级依赖注入框架,目前需要的jdk版本为1.6及以上。Guice实现依赖注入,首先在Module中通过bind方法绑定对象实例、或者绑定映射到对应接口对象实例(这一步可以理解为在Spring配置文件中配置让容器托管的@Bean对象),然后在需要其他对象的地方增加@Inject的注释,就可以自动将对应对象实例注入进来。关于Inject和bind,后面的例子中再慢慢理解。

2. 依赖注入

既然Guice是轻量级的依赖注入框架,那么还是先简单的回顾一下控制反转和依赖注入的概念。控制反转(Inversion of control)是一种软件设计原则,通过控制反转,可以让计算机程序的自定义业务代码,接收来自通用框架的控制流程。控制反转是相对于传统的程序开发来讲的:在传统的程序开发中,自定义业务代码调用可重用的框架类库,来实现自定义业务代码的功能。通过控制反转,将由框架来调用自定义业务代码。也就是说,这个调用的过程原本是自定义业务代码调用框架,现在反转了,反过来由框架调用自定义业务代码来实现功能,最终实现高内聚,低耦合,可扩展的系统。

依赖注入是控制反转的一种实现方式。通俗一点来说,假如要实现一个功能,A类需要调用B类的方法,那么,我们就可以说,此时A类依赖于B类。在传统方式下,需要在A类代码中手动new 一个B类的实例,赋值给A类的某个字段。项目大了,各种依赖错综复杂,耦合度太高,不利于项目的维护。就需要通过程序框架,将B类的实例注入给A类,达到自动装配各类间实例依赖的效果。在Spring中,通过容器来管理各个类实例,实现自动注入,那么Guice如何实现呢?我们后面慢慢来讲。

3. Guice使用方法

3.1 依赖注入之类实例托管给容器

上面我们已经说过,Spring中,依赖注入是将类实例交给容器来进行依赖注入的,Guice却没有容器的概念。无论是否有容器这个概念,根据控制反转的定义,总归我们的自定义业务编码,将要接受通用框架的控制,我们自定义业务编码依赖的实例对象,也将要接受通用框架的注入。在Guice中,可以将Module理解为实施控制和注入的通用框架,我们业务代码所依赖的各个类实例,将有Module来注入。既然由Module来注入我们的类实例,首先,我们需要将所有的类实例注册到Module里面,这样,Module才有资源进行注入。如下代码分别描述了将一个实例对象、绑定了接口的实例对象注册到Module中的过程。其中的in里面的代码表示该实例对象的类型为单例实例。

222256_9k9V_1170760.png

通过以上的代码,我们就可以将InstanceServiceImpl类实例和DemoServiceImpl类的实例交给Module来管理。所以Module类,就可以抽象的理解为一个小容器。同一模块或者同种类型的类,可以交给同一个Module类管理,这样更能实现清晰的业务编码逻辑。

Guice在项目启动的时候,可以通过如下代码,创建很多个Module实例

222401_7Wfh_1170760.png

通过injector对象,我们也可以显式的获取到注册到Module的类实例:

222422_dgml_1170760.png

3.2 绑定方式
  • 实例绑定
bind(String.class)
        .annotatedWith(Names.named("jdbc_url"))
        .toInstance("jdbc:mysql://localhost/pizza");

如上代码,实例绑定,可以理解为,在Module管理的容器中,实例化一个String字符串,命名为JDBC URL,赋值jdbc:mysql://localhost/pizza 在需要注入的地方,使用

222738_rMyf_1170760.png

即可将绑定的实例对象注入。

  • 链式绑定
// 将DemoServiceImpl的类实例,绑定到接口IDemoService上
bind(IDemoService.class).to(DemoServiceImpl.class).in(Scopes.SINGLETON);

链式绑定将实现和接口链接起来,当使用injector.getInstance(IDemoService.class)方法来获取实例,或者通过构造函数注入IDemoService类型的时候,系统将实例化一个DemoServiceImpl对象,将对象注入。

  • 注释绑定

有时候,一个接口,我们可能会有多种实现。那么就可以通过这种注释绑定的方式,来标明绑定哪种类型,进而注入明确的类型。拿Guice github wiki上面的例子来做个说明。首先需要使用Guice的@BindingAnnotation来创建一个注释类型:

@BindingAnnotation @Target({ FIELD, PARAMETER, METHOD }) @Retention(RUNTIME)
public @interface PayPal {}

然后,在Module注册的时候,通过如下代码,将PayPalCreditCardProcessor绑定到CreditCardProcessor类型,同时,使用PayPal 这个注释:

bind(CreditCardProcessor.class)
        .annotatedWith(PayPal.class)
        .to(PayPalCreditCardProcessor.class);

这样,在需要注入CreditCardProcessor类型的地方,使用@ PayPal注释一下,就会自动注入PayPalCreditCardProcessor实例。

223345_T70X_1170760.png

  • @Provides Methods/ ProviderBindings

在链式绑定的时候,我们通过bind(IDemoService.class).to(DemoServiceImpl.class)样的形式,将IDemoService和其实现DemoServiceImpl绑定并注册到Module。如果没有必要专门写一个实现类或者想明确制定一个Provider,就可以通过在Module中,使用@Provides注释,进行标记返回服务提供者:

public class BillingModule extends AbstractModule {
  @Override
  protected void configure() {
    // .....
  }

  @Provides
  IDemoService provideDemoService() {
     return new IDemoService() {
          @Override
          public void doSomeThing() {
          }
     }
  }
}

通过这样@Provides的方式,就可以提供IDemoService类型注入的实例。但是这样的@Provides写的多了,在Module类中将难以维护,所以,类似这样的@Provides可以统一实现Guice提供的

public interface Provider<T> {
  T get();
}

接口,然后通过如下代码进行绑定:

public class BillingModule extends AbstractModule {
  @Override
  protected void configure() {
    bind(IDemoService.class)
        .toProvider(DemoServiceProvider.class);
  }
}

即为ProviderBindings绑定。

3.3 注入方式

通过以上绑定方式,将各种类型的类实例注册到Module之后,就可以用@Inject注释进行注入管理了。注入使用起来也比较简单,包括构造函数注入,属性注入,函数注入等,在需要注入的地方,加上@Inject注释即可自动注入。

  • 构造注入
class BillingService {
  private final CreditCardProcessor processor;
  private final TransactionLog transactionLog;

  @Inject
  BillingService(CreditCardProcessor processor, 
      TransactionLog transactionLog) {
    this.processor = processor;
    this.transactionLog = transactionLog;
  }

  public Receipt chargeOrder(PizzaOrder order, CreditCard creditCard) {
    ...
  }
}
  • 属性注入
public class DemoServiceImpl implements IDemoService {
	
	// 通过@Inject注入instanceServiceImpl实例
	@Inject
	private InstanceServiceImpl instanceServiceImpl;
	
	@Override
	public void doSomeThing() {
		//调用instanceServiceImpl方法
		instanceServiceImpl.doFirstThing();
		
		// ....其他逻辑
	}
}
  • 函数注入
public class DemoServiceImpl implements IDemoService {
	
	private InstanceServiceImpl instanceServiceImpl;

	@Inject
	public void setInstanceServiceImpl(InstanceServiceImpl instanceServiceImpl) {
		this.instanceServiceImpl = instanceServiceImpl;
	}

	@Override
	public void doSomeThing() {
		instanceServiceImpl.doFirstThing();
	}
	
	public InstanceServiceImpl getInstanceServiceImpl() {
		return instanceServiceImpl;
	}
}
3.4 加载配置文件并实现静态字段注入

一般我们在项目中,会写很多如下静态字段,便于方法调用。像这样的静态字段,也可以通过@Inject注入。但是一般情况下,这样的属性都是配置在配置文件中,该如何进行注入呢?

224312_mKMd_1170760.png

如上,我们可以通过@Inject@Named组合进行注入。同时,Guice也提供了将Properties对象转化为Named实例等方法。

public class CommandModule implements Module {

	private static final Logger logger = LoggerFactory.getLogger(CommandModule.class);

	@Override
	public void configure(Binder binder) {
		// 加载配置文件
		try {
			loadProperties(binder);
		} catch (FroadException e) {
			logger.error("加载配置文件app.properties失败", e);
			return;
		}
		// 给静态字段注入配置文件中配置常量
		binder.requestStaticInjection(URLCommand.class);
	}

	/**
	 * 加载配置文件
	 * @param binder
	 * @throws FroadException
	 */
	private void loadProperties(Binder binder) throws FroadException {
		InputStream inputStream = null;
		Properties properties = new Properties();
		try {
			logger.info("开始加载配置文件app.properties");
			String configPath = System.getProperty("PROJECTDIR") + File.separator + "config";
			File configFile = new File(configPath);
			if (!configFile.exists()) {
				logger.error("配置文件路径config不存在");
				throw new FroadException("配置文件路径config不存在!");
			}
			String appPropertiesPath = configPath + File.separator + "app.properties";
			File appPropertiesFile = new File(appPropertiesPath);
			if (!appPropertiesFile.exists()) {
				logger.error("配置文件app.properties不存在");
				throw new FroadException("配置文件app.properties不存在!");
			}
			logger.info("读取配置文件app.properties");
			inputStream = new FileInputStream(appPropertiesFile);
			properties.load(inputStream);
			Names.bindProperties(binder, properties);
			logger.info("加载配置文件app.properties完毕");
		} catch (IOException e) {
			logger.error("解析app.properties文件失败", e);
			binder.addError(e);
			throw new FroadException("加载配置文件app.properties失败!");
		} finally {
			try {
				if (inputStream != null) {
					inputStream.close();
				}
			} catch (IOException e) {
				logger.error("关闭app文件IO流失败", e);
			}
		}
	}

}

 

4.总结

通过以上的方法,我们可以将不同类型的对象实例注册给Module对象,在需要的地方使用@Inject@Named,即可非常方便的将实例注入到依赖该对象的类中。下一节将讲解如何在Guice中整合Mybatis。

转载于:https://my.oschina.net/kalo/blog/867996

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值