Guice是Google开源的一个依赖注入类库,相比于Spring IoC来说更小更快。Elasticsearch大量使用了Guice,本文简单的介绍下Guice的基本概念和使用方式

基本使用

1. 引入依赖

如果使用gradle的话,添加下列依赖

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了

项目骨架

我们来假设一个简单的项目框架。首先我们需要一个业务接口,简单的包含一个方法用于执行业务逻辑。
它的实现也非常简单

// UserService接口
public interface UserService {void process();
}// 实现类
public class UserServiceImpl implements UserService {@Overridepublic void process() {System.out.println("我需要做一些业务逻辑");}
}

然后我们需要一个日志接口,它和它的实现也非常简单

// 日志接口
public interface LogService {void log(String msg);
}// 实现类
public class LogServiceImpl implements LogService {@Overridepublic void log(String msg) {System.out.println("------LOG:" + msg);}
}

最后是一个系统接口和相应的实现。
在实现中我们使用了业务接口和日志接口处理业务逻辑和打印日志信息。

public interface Application {void work();
}public class MyApp implements Application {private UserService userService;private LogService logService;@Injectpublic MyApp(UserService userService, LogService logService) {this.userService = userService;this.logService = logService;}@Overridepublic void work() {userService.process();logService.log("程序正常运行");}
}

在 MyApp 类中定义了 UserService 和 LogService 两个变量,但是还没有给它们创建对象,而 word 方法中分别调用了 process 和 log 方法,它们的实际执行结果由最终注入的对象决定

简单的依赖注入

首先来配置依赖关系。我们继承AbstractModule类,并重写configure方法即可。在configure方法中,我们可以调用AbstractModule类提供的一些方法来配置依赖关系。

最常用的方式就是 bind(接口或父类).to(实现类或子类) 的方式来设置依赖关系

public class MyAppModule extends AbstractModule {@Overrideprotected void configure() {bind(LogService.class).to(LogServiceImpl.class);bind(UserService.class).to(UserServiceImpl.class);bind(Application.class).to(MyApp.class);}
}

这样一来,当Guice遇到接口或父类需要注入具体实现的时候,就会使用这里配置的实现类或子类来注入
如果希望在构造器中注入依赖的话,只需要添加 @Inject 注解即可

Guice配置完之后,我们需要调用 Guice.createInjector 方法传入配置类来创建一个注入器,然后使用注入器的 getInstance 方法获取目标类,Guice会按照配置帮我们注入所有依赖。

我们使用单元测试来看看效果

public class MyAppTest {private static Injector injector;@BeforeClasspublic static void init() {injector = Guice.createInjector(new MyAppModule());}@Testpublic 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 {@Injectpublic 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 {@Injectpublic 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 {@Overrideprotected void configure() {...}@ProvidesTransactionLog provideTransactionLog() {DatabaseTransactionLog transactionLog = new DatabaseTransactionLog();transactionLog.setJdbcUrl("jdbc:mysql://localhost/pizza");transactionLog.setThreadPoolSize(30);return transactionLog;}
}

@Provides 方法也可以应用 @Named 和自定义注解,还可以注入其他依赖,Guice会在调用方法之前注入需要的对象

  @Provides @PayPalCreditCardProcessor 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;@Injectpublic 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 {@Overrideprotected 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 @SingletonTransactionLog 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 {@Overrideprotected 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 {@Overrideprotected void configureServlets() {serve("/*").with(MainServlet.class);filter("/*").through(CharEncodingFilter.class);}
}

然后将ServletModule替换为我们自己的实现类

public class MyGuiceConfigListener extends GuiceServletContextListener {@Overrideprotected Injector getInjector() {return Guice.createInjector(new MyServletConfig());}
}

注入Servlet相关对象

除了配置Servlet之外,Guice还允许我们把Request、Response和Session对象注入到非Servlet对象中。下面是Guice的一个例子

@RequestScoped
class SomeNonServletPojo {@InjectSomeNonServletPojo(HttpServletRequest request, HttpServletResponse response, HttpSession session) {...}
}

我们还可以使用Guice注入请求参数。下面这个类的作用是获取所有请求参数并转换为字符串形式。

@RequestScoped
public class Information {@Inject@RequestParametersMap<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 {@Injectprivate Injector injector;@Overrideprotected 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);}@Overridepublic 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

Google Guice 快速入门相关推荐

  1. Google Guice使用入门

    2019独角兽企业重金招聘Python工程师标准>>> 本文通过范例简单地介绍Google Guice的使用,通过下面的范例我们可以知道,Google Guice的使用非常简单. G ...

  2. Google Guice使用入门(转)

    本文通过范例简单地介绍Google Guice的使用,通过下面的范例我们可以知道,Google Guice的使用非常简单. Google Guice需要使用JDK1.5以上java环境. 下载Goog ...

  3. Google Guice范例解说之使用入门

    http://www.cnblogs.com/xd502djj/archive/2012/06/25/2561414.html Google Guice范例解说之使用入门 http://code.go ...

  4. 机器学习——Google 快速入门课程(综合版)

    前言 本文参考 Google 谷歌官网机器学习的快速入门课程,整体课程比较好理解,供大家学习参考:文章也会结合自己的理解进行优化.看到官网的消息2021/7之后就不提供中文版的机器学习快速入门课程了, ...

  5. GEE(Google Earth Engine) 代码学习笔记一 快速入门

    GEE 代码学习笔记一 (GEE 基于JavaScript语言和python语言,我记录的是JavaScript语言) 1.GEE 快速入门 quick start. 2.基本语句 - 简单输出 pr ...

  6. 庖丁解牛剖析国际学术论文写作的快速入门

    2020年3月17日,在超千人同时在线的"智源论坛·论文写作专题报告会"上,三位智源青年科学家分别进行了精彩的在线分享,题目分别为:中国科学院计算技术研究所研究员兰艳艳<论文 ...

  7. BERT模型超酷炫,上手又太难?请查收这份BERT快速入门指南!

    点击上方"AI遇见机器学习",选择"星标"公众号 重磅干货,第一时间送达 来自 | GitHub    作者 | Jay Alammar 转自 | 机器之心 如 ...

  8. BERT模型超酷炫,上手又太难?请查收这份BERT快速入门指南

    2019-12-31 10:50:59 选自GitHub 作者:Jay Alammar 参与:王子嘉.Geek AI 如果你是一名自然语言处理从业者,那你一定听说过最近大火的 BERT 模型.本文是一 ...

  9. python快速编程入门教程-半小时带你快速入门Python编程,Python快速入门教程

    1,Introduction to Python (Python入门) 2,Python是什么? Python 官方网站的描述 Python is a programming language tha ...

  10. python快速自学方式-Python自学之路 ,自学Python快速入门方法

    "'坚持不是一件容易的事情,兴趣是最好的老师"',等你坚持过后你总会这么对别人侃侃而谈. 这篇文章不是教大家python语法,也不是告诉大家某个关键字如何使用,主要说的是一种方法, ...

最新文章

  1. TCGAbiolinks包分析TCGA数据
  2. keras第二课:后端函数
  3. layer output 激活函数_一文彻底搞懂BP算法:原理推导+数据演示+项目实战(下篇)...
  4. .Net Core扩展 SharpPlugs简单上手
  5. TiDB 在知乎万亿量级业务数据下的实践和挑战
  6. Axure RP使用攻略--入门级(五)系统函数与变量
  7. 如何加快Simulink模型的仿真速度
  8. Doom 规律+大数
  9. shiro的内部体系结构
  10. 手动清空Element Select选择框内容 v-model 和 option下拉框选项 导致无法select选中,或者选中视图不渲染问题,
  11. 【BZOJ】【1015】 【JSOI2008】星球大战starwar
  12. lLinux系统安全sudo+pam
  13. 【算法笔记】B1040 有几个PAT
  14. 99行代码的《冰雪奇缘》,收下我的膝盖!
  15. 两栏 论文首页 插尾注方法 word2019
  16. 青出于蓝而胜于蓝 论AI大公司是拼不过小创企的
  17. Correct way to wait for VBLANK on windows 10 in windowed mode
  18. 昔日移动GPU王者,从数据中心、汽车等市场全面杀回来——专访Imagination技术创新副总裁Kristof Beets...
  19. 遇见2016年最值得期待的新产品
  20. android u盘挂载监听,Android SD卡及U盘插拔状态监听及内容读取

热门文章

  1. 互联网晚报 | 8月22日 星期日 | 抖音回应腾讯《扫黑风暴》相关投诉;比亚迪半导体被中止上市审核;三星正式推出UPC技术...
  2. Android--使用开源vitamio做万能视频播放器
  3. VNPY_IB API封装
  4. 程序员必备的画图工具汇总
  5. 交互式反汇编器 linux,Carbon:交互式反汇编工具
  6. UEFI学习——使用gRT->GetVariable读取Setup选项值
  7. 帝国cms插件-百度收录api模式插件
  8. EXCEL【数据处理之数据抽取——随机抽样】
  9. 在走迷宫任务中实现强化学习(持续更新中)——第二课:移动体的路径规划(小川雄太郎《边做边学深度强化学习》项目复刻)
  10. 计算机网络实验设计-Tracert 与Ping 程序设计与实现