Guice是Google开源的一个依赖注入类库,相比于Spring IoC来讲更小更快。Elasticsearch大量使用了Guice,本文简单的介绍下Guice的基本概念和使用方式css
基本使用
1. 引入依赖
若是使用gradle的话,添加下列依赖html
compile group: 'com.google.inject.extensions', name: 'guice-multibindings', version: '4.2.0'
compile group: 'com.google.inject', name: 'guice', version: '4.2.0'
当构建工具解决完项目的依赖以后,咱们就能够开始使用Guice了java
项目骨架
咱们来假设一个简单的项目框架。首先咱们须要一个业务接口,简单的包含一个方法用于执行业务逻辑。
它的实现也很是简单mysql
// UserService接口
public interface UserService {
void process();
}
// 实现类
public class UserServiceImpl implements UserService {
@Override
public void process() {
System.out.println("我须要作一些业务逻辑");
}
}
而后咱们须要一个日志接口,它和它的实现也很是简单web
// 日志接口
public interface LogService {
void log(String msg);
}
// 实现类
public class LogServiceImpl implements LogService {
@Override
public void log(String msg) {
System.out.println("------LOG:" + msg);
}
}
最后是一个系统接口和相应的实现。
在实现中咱们使用了业务接口和日志接口处理业务逻辑和打印日志信息。sql
public interface Application {
void work();
}
public class MyApp implements Application {
private UserService userService;
private LogService logService;
@Inject
public MyApp(UserService userService, LogService logService) {
this.userService = userService;
this.logService = logService;
}
@Override
public void work() {
userService.process();
logService.log("程序正常运行");
}
}
在 MyApp 类中定义了 UserService 和 LogService 两个变量,可是尚未给它们建立对象,而 word 方法中分别调用了 process 和 log 方法,它们的实际执行结果由最终注入的对象决定api
简单的依赖注入
首先来配置依赖关系。咱们继承AbstractModule
类,并重写configure
方法便可。在configure
方法中,咱们能够调用AbstractModule
类提供的一些方法来配置依赖关系。安全
最经常使用的方式就是 bind(接口或父类).to(实现类或子类)
的方式来设置依赖关系。微信
public class MyAppModule extends AbstractModule {
@Override
protected void configure() {
bind(LogService.class).to(LogServiceImpl.class);
bind(UserService.class).to(UserServiceImpl.class);
bind(Application.class).to(MyApp.class);
}
}
这样一来,当Guice遇到接口或父类须要注入具体实现的时候,就会使用这里配置的实现类或子类来注入。
若是但愿在构造器中注入依赖的话,只须要添加 @Inject
注解便可session
Guice配置完以后,咱们须要调用 Guice.createInjector
方法传入配置类来建立一个注入器,而后使用注入器的 getInstance
方法获取目标类,Guice会按照配置帮咱们注入全部依赖。
咱们使用单元测试来看看效果
public class MyAppTest {
private static Injector injector;
@BeforeClass
public static void init() {
injector = Guice.createInjector(new MyAppModule());
}
@Test
public void testMyApp() {
Application myApp = injector.getInstance(Application.class);
myApp.work();
}
}
//程序结果
//我须要作一些业务逻辑
//------LOG:程序正常运行
依赖绑定
下面这些例子都是Guice文档上的例子
链式绑定
咱们在绑定依赖的时候不只能够将父类和子类绑定,还能够将子类和更具体的子类绑定。
下面的例子中,当咱们须要 TransactionLog
的时候,Guice最后会为咱们注入 MySqlDatabaseTransactionLog
对象。
// 链式绑定:TransactionLog -> DatabaseTransactionLog -> MySqlDatabaseTransactionLog
public class BillingModule extends AbstractModule {
@Override
protected void configure() {
bind(TransactionLog.class).to(DatabaseTransactionLog.class);
bind(DatabaseTransactionLog.class).to(MySqlDatabaseTransactionLog.class);
}
}
注解绑定
当咱们须要将多个同一类型的对象注入不一样对象的时候,就须要使用注解区分这些依赖了。
最简单的办法就是使用 @Named
注解进行区分
首先须要在要注入的地方添加 @Named
注解
public class RealBillingService implements BillingService {
@Inject
public RealBillingService(@Named("Checkout") CreditCardProcessor processor,
TransactionLog transactionLog) {
...
}
而后在绑定中添加 annotatedWith
方法指定 @Named
中指定的名称。
因为编译器没法检查字符串,因此Guice官方建议咱们保守地使用这种方式
bind(CreditCardProcessor.class)
.annotatedWith(Names.named("Checkout"))
.to(CheckoutCreditCardProcessor.class);
若是但愿使用类型安全的方式,能够自定义注解
@BindingAnnotation
@Target({ FIELD, PARAMETER, METHOD })
@Retention(RUNTIME)
public @interface PayPal {
}
而后在须要注入的类上应用
public class RealBillingService implements BillingService {
@Inject
public RealBillingService(@PayPal CreditCardProcessor processor,
TransactionLog transactionLog) {
...
}
在配置类中,使用方法也和@Named相似
bind(CreditCardProcessor.class)
.annotatedWith(PayPal.class)
.to(PayPalCreditCardProcessor.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);
若是使用 toInstance
方法注入的实例比较复杂的话,可能会影响程序启动。这时候可使用 @Provides
方法代替
@Provides方法
当一个对象很复杂,没法使用简单的构造器来生成的时候,咱们可使用 @Provides
方法,也就是在配置类中生成一个注解了 @Provides
的方法。
在该方法中咱们能够编写任意代码来构造对象
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
方法也能够应用 @Named
和自定义注解,还能够注入其余依赖,Guice会在调用方法以前注入须要的对象
@Provides
@PayPal
CreditCardProcessor providePayPalCreditCardProcessor(
@Named("PayPal API key") String apiKey) {
PayPalCreditCardProcessor processor = new PayPalCreditCardProcessor();
processor.setApiKey(apiKey);
return processor;
}
Provider绑定
若是项目中存在多个比较复杂的对象须要构建,使用 @Provide
方法会让配置类变得比较乱。咱们可使用Guice提供的 Provider接口
将复杂的代码放到单独的类中。办法很简单,实现 Provider<T>
接口的get方法便可。在 Provider
类中,咱们可使用 @Inject
任意注入对象
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;
}
}
在配置类中使用 toProvider
方法绑定到 Provider
上便可
public class BillingModule extends AbstractModule {
@Override
protected void configure() {
bind(TransactionLog.class)
.toProvider(DatabaseTransactionLogProvider.class);
}
做用域
默认状况下Guice会在每次注入的时候建立一个新对象。若是但愿建立一个单例依赖的话,能够在实现类上应用 @Singleton
注解
@Singleton
public class InMemoryTransactionLog implements TransactionLog {
/* everything here should be threadsafe! */
}
或者也能够在配置类中指定
bind(TransactionLog.class).to(InMemoryTransactionLog.class).in(Singleton.class);
在@Provides方法中也能够指定单例
@Provides
@Singleton
TransactionLog provideTransactionLog() {
...
}
若是一个类型上存在多个冲突的做用域,Guice会使用 bind()
方法中指定的做用域。若是不想使用注解的做用域,能够在 bind()
方法中将对象绑定为 Scopes.NO_SCOPE
Guice和它的扩展提供了不少做用域,有单例Singleton,Session做用域SessionScoped,Request请求做用域RequestScoped等等。咱们能够根据须要选择合适的做用域
Servlet集成
Guice也能够和Servlet项目集成,这样咱们就能够不用编写冗长的 web.xml
,以依赖注入的方式使用Servlet和相关组件
安装Guice Servlet扩展
要在Servlet项目中集成Guice,首先须要安装Guice Servlet扩展。若是使用Maven,添加下面的依赖。
compile group: 'com.google.inject.extensions', name: 'guice-servlet', version: '4.2.0'
加依赖以后,在 web.xml
中添加下面的代码,让Guice过滤全部web请求
<filter>
<filter-name>guiceFilter</filter-name>
<filter-class>com.google.inject.servlet.GuiceFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>guiceFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
配置Injector
和普通的项目同样,Servlet项目一样须要配置Injector。一种比较好的办法就是在 ContextListener
中配置 Injector
。Guice的Servlet集成提供了 GuiceServletContextListener
,咱们继承该类并在 getInjector
方法中配置 Injector
便可。
public class MyGuiceServletConfigListener extends GuiceServletContextListener {
@Override
protected Injector getInjector() {
return Guice.createInjector(new ServletModule());
}
}
固然仅仅继承 GuiceServletContextListener
仍是不够的。咱们还须要在 web.xml
中添加几行代码
<listener>
<listener-class>yitian.study.servlet.MyGuiceConfigListener</listener-class>
</listener>
配置Servlet和过滤器
默认的ServletModule就会启用一些功能。若是须要自定义Servlet和Filter,就须要继承ServletModule并重写configureServlets()方法。配置Servlet和Filter的方法和配置普通依赖注入相似
public class MyServletConfig extends ServletModule {
@Override
protected void configureServlets() {
serve("/*").with(MainServlet.class);
filter("/*").through(CharEncodingFilter.class);
}
}
而后将ServletModule替换为咱们本身的实现类
public class MyGuiceConfigListener extends GuiceServletContextListener {
@Override
protected Injector getInjector() {
return Guice.createInjector(new MyServletConfig());
}
}
注入Servlet相关对象
除了配置Servlet以外,Guice还容许咱们把Request、Response和Session对象注入到非Servlet对象中。下面是Guice的一个例子
@RequestScoped
class SomeNonServletPojo {
@Inject
SomeNonServletPojo(HttpServletRequest request, HttpServletResponse response, HttpSession session) {
...
}
}
咱们还可使用Guice注入请求参数。下面这个类的做用是获取全部请求参数并转换为字符串形式。
@RequestScoped
public class Information {
@Inject
@RequestParameters
Map<String, String[]> params;
public String getAllParameters() {
return params.entrySet()
.stream()
.map(entry -> entry.getKey() + " : " + Arrays.toString(entry.getValue()))
.collect(Collectors.joining(", "));
}
}
以后,咱们就能够将该对象注入到Servlet中使用,将结果返回给页面
@Singleton
public class MainServlet extends HttpServlet {
@Inject
private Injector injector;
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String name = req.getParameter("name");
req.setAttribute("name", name);
Information info = injector.getInstance(Information.class);
req.setAttribute("params", info.getAllParameters());
req.getRequestDispatcher("index.jsp").forward(req, resp);
}
@Override
public void init() throws ServletException {
super.init();
}
}
Guice Servlet扩展还有其余功能,这里就不在列举了。详情请参看Guice文档
JSR-330标准
JSR-330是一项Java EE标准,指定了Java的依赖注入标准。Spring、Guice和Weld等不少框架都支持JSR-330。下面这个表格来自于Guice文档,咱们能够看到JSR-330和Guice注解基本上能够互换。

Guice官方推荐咱们首选JSR-330标准的注解。
以上就是Guice的基本知识了。若是须要更详细的使用方法,请参考Guice文档
本文代码可在 CSDN code 下载
本文转载自:http://www.what21.com/article/b_java_1491385837669.html
本文分享自微信公众号 - 小旋锋(whirlysBigData)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。