- 浏览: 154090 次
- 性别:
- 来自: 上海
文章分类
最新评论
-
chuanwang66:
同2楼啊,试过更大规模的数据量吗:)
[转]Berkeley DB Java Edition使用说明 -
smalltiger1984:
直接字符串比较就行了,哪用这么麻烦
javascript 比较开始时间和结束时间 -
windows1987:
今天我在一本叫做《编程之美-微软技术面试心得》这本书上看到了这 ...
[转]5只蚂蚁的问题 -
gainfirst:
我的思路是定义了一个木头类和蚂蚁类,蚂蚁碰头掉转方向的依据是当 ...
[转]5只蚂蚁的问题 -
gainfirst:
很有意思,我自己也写了个,写完后看了下你的实现思路算是差 ...
[转]5只蚂蚁的问题
Guice 1.0 用户指南
原文地址: http://docs.google.com/Doc?id=dqp53rg_3hjf3ch
(20070326 王咏刚 译自:http://docs.google.com/Doc?id=dd2fhx4z_5df5hw8)
Guice (读作"juice")是超轻量级的,下一代的,为Java 5及后续版本设计的依赖注入容器。
简介
Java企业应用开发社区在连接对象方面花了很大功夫。你的Web应用如何访问中间层服务?你的服务如何连接到登录用户和交易管理器?关于这个问题你会发 现很多通用的和特定的解决方案。有一些方案依赖于模式,另一些则使用框架。所有这些方案都会不同程度地引入一些难于测试或者程式化代码重复的问题。你马上 就会看到,Guice 在这方面是全世界做得最好的:非常容易进行单元测试,最大程度的灵活性和可维护性,以及最少的代码重复。
我们使用一个假想的、简单的例子来展示 Guice 优于其他一些你可能已经熟悉的经典方法的地方。下面的例子过于简单,尽管它展示了许多显而易见的优点,但其实它还远没有发挥出 Guice 的全部潜能。我们希望,随着你的应用开发的深入,Guice 的优越性也会更多地展现出来。
在这个例子中,一个客户对象依赖于一个服务接口。服务接口可以提供任意的服务。我们只是调用这个服务而已。
public interface Service {
void go();
}
对于这个服务接口,我们有一个缺省的实现,但客户对象不应该直接依赖于这个缺省实现。如果我们将来打算使用一个不同的服务实现,我们不希望回过头来修改所有的客户代码。
public class ServiceImpl implements Service {
public void go() {
...
}
}
我们还有一个虚拟的、可用于单元测试的服务实现。
public class MockService implements Service {
private boolean gone = false;
public void go() {
gone = true;
}
public boolean isGone() {
return gone;
}
}
简单工厂模式
private ServiceFactory() {}
private static Service instance = new ServiceImpl();
public static Service getInstance() {
return instance;
}
public static void setInstance(Service service) {
instance = service;
}
}
客户程序每次需要服务对象时就直接从工厂获取。
public void go() {
Service service = ServiceFactory.getInstance();
service.go();
}
}
客户程序足够简单。但客户程序的单元测试代码必须将一个虚拟对象传入工厂,同时要记得在测试后清理。在我们这个简单的例子里,这不算什么难事儿。但当你增 加了越来越多的客户和服务代码后,所有这些虚拟代码和清理代码会让单元测试的开发一团糟。此外,如果你忘记在测试后清理,其他测试可能会得到与预期不符的 结果。更糟的是,测试的成功与失败可能取决于他们被执行的顺序。
public void testClient() {
Service previous = ServiceFactory.getInstance();
try {
final MockService mock = new MockService();
ServiceFactory.setInstance(mock);
Client client = new Client();
client.go();
assertTrue(mock.isGone());
}
finally {
ServiceFactory.setInstance(previous);
}
}
最后,注意服务工厂的API把我们限制在了单件这一种应用模式上。即便 getInstance() 可以返回多个实例, setInstance() 也会束缚我们的手脚。转换到非单件模式也意味着转换到了一套更复杂的API。
手工依赖注入
当上例中的客户代码向工厂对象请求一个服务时,根据依赖注入模式,客户代码希望它所依赖的对象实例可以被传入自己。也就是说:不要调用我,我会调用你。
private final Service service;
public Client(Service service) {
this.service = service;
}
public void go() {
service.go();
}
}
MockService mock = new MockService();
Client client = new Client(mock);
client.go();
assertTrue(mock.isGone());
}
现在,我们如何把客户代码连接到服务对象呢?手工实现依赖注入的时候,我们可以将所有依赖逻辑都移动到工厂类中。也就是说,我们还需要有一个工厂类来创建客户对象。
private ClientFactory() {}
public static Client getInstance() {
Service service = ServiceFactory.getInstance();
return new Client(service);
}
}
用 Guice 实现依赖注入
Guice 希望在不牺牲可维护性的情况下去除所有这些程式化的代码。
使用 Guice,你只需要实现模块类。Guice 将一个绑定器传入你的模块,你的模块使用绑定器来连接接口和实现。以下模块代码告诉 Guice 将 Service 映射到单件模式的 ServiceImpl:
public void configure(Binder binder) {
binder.bind(Service.class)
.to(ServiceImpl.class)
.in(Scopes.SINGLETON);
}
}
private final Service service;
@Inject
public Client(Service service) {
this.service = service;
}
public void go() {
service.go();
}
}
为了让 Guice 向 Client 中注入,我们必须直接让 Guice 帮我们创建 Client 的实例,或者,其他类必须包含被注入的 Client 实例。
Guice vs. 手工依赖注入
Guice 允许你通过声明指定对象的作用域。例如,你不用为了将对象存入 HttpSession 而反复编写同样的代码。
在现实世界,不到运行的时候,你通常并不知道具体要使用哪一个实现类。你需要元工厂类或是服务定位器来增强你的工厂模式。Guice 用最少的代价解决了这些问题。
手工实现依赖注入时,你很容易退回到使用直接依赖的旧习惯,特别是当你对依赖注入的概念还不那么熟悉的时候。使用 Guice 可以避免这种问题,可以让你更容易地把事情做对。Guice 使你保持正确的方向。
更多的标注
public interface Service {
void go();
}
缺省情况下,Guice 每次都注入一个新的实例。如果你想指定不同的作用域规则,你也可以对实现类进行标注。
public class ServiceImpl implements Service {
public void go() {
...
}
}
架构概览
启动
public void configure(Binder binder) {
// Bind Foo to FooImpl. Guice will create a new
// instance of FooImpl for every injection.
binder.bind(Foo.class).to(FooImpl.class);
// Bind Bar to an instance of Bar.
Bar bar = new Bar();
binder.bind(Bar.class).toInstance(bar);
}
}
创建一个 Injector 涉及以下步骤:
- 首先创建你的模块类的实例,并将其传入 Guice.createInjector().
- Guice 创建一个绑定器 Binder 并将其传入你的模块。
- 你的模块使用绑定器来定义绑定。
- 基于你所定义的绑定,Guice 创建一个注入器 Injector 并将其返回给你。
- 你使用注入器来注入对象。
运行
每个绑定有一个提供者 provider,它提供所需类型的实例。你可以提供一个类,Guice 会帮你创建它的实例。你也可以给 Guice 一个你要绑定的实例。你还可以实现你自己的 provider,Guice 可以向其中注入依赖关系。
每一个绑定还有一个可选的作用域。缺省情况下绑定没有作用域,Guice 为每一次注入创建一个新的对象。一个定制的作用域可以使你控制 Guice 创建在何时创建新对象。例如,你可以为每一个 HttpSession 创建一个实例。
自举(Bootstrapping)你的应用
你的代码应该尽量少地和 Injector 直接打交道。相反,你应该通过注入一个根对象来自举你的应用。容器可以更进一步地将依赖注入根对象所依赖的对象,并如此迭代下去。最终,在理想情况下,你的应用中应该只有一个类知道 Injector,每个其他类都应该使用注入的依赖关系。
例如,一个诸如 Struts 2 的 Web 应用框架通过注入所有你的动作类来自举你的应用。你可以通过注入你的服务实现类来自举一个 Web 服务框架。
依赖注入是传染性的。如果你重构一个有大量静态方法的已有代码,你可能会觉得你正在试图拉扯一根没有尽头的线。这是好事情。它表明依赖注入正在帮助你改进代码的灵活性和可测试性。
如果重构工作太复杂,你不想一次性地整理完所有代码,你可以暂时将一个 Injector 的引用存入某个类的一个静态的字段,或是使用静态注入。这时,请清楚地命名包含该字段的类:比如 InjectorHack 和 GodKillsAKittenEveryTimeYouUseMe。记住你将来可能不得不为这些类提供虚拟类,你的单元测试则不得不手工安装一个注入器。记住,你将来需要清理这些代码。
绑定依赖关系
当注入依赖关系时,Guice 首先查看显式绑定,即你通过绑定器 Binder 指明的绑定。Binder API 使用生成器(Builder)模式来创建一种领域相关的描述语言。根据约束适用方法的上下文的不同,不同方法返回不同的对象。
例如,为了将接口 Service 绑定到一个具体的实现 ServiceImpl,调用:
void injectService(Service service) {
...
}
注: 与某些其他的框架相反,Guice 并没有给 "setter" 方法任何特殊待遇。不管方法有几个参数,只要该方法含有 @Inject 标注,Guice 就会实施注入,甚至对基类中实现的方法也不例外。
不要重复自己
在本手册的余下部分中我们会一直使用这样的语法。
标注绑定
.annotatedWith(Blue.class)
.to(BlueService.class);
void injectService(@Blue Service service) {
...
}
创建绑定标注
刚才提到的标注 @Blue 是从哪里来的?你可以很容易地创建这种标注,但不幸的是,你必须使用略显复杂的标准语法:
* Indicates we want the blue version of a binding.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.PARAMETER})
@BindingAnnotation
public @interface Blue {}
幸运的是,我们不需要理解这些代码,只要会用就可以了。对于好奇心强的朋友,下面是这些程式化代码的含义:
- @Retention(RUNTIME) 使得你的标注在运行时可见。
- @Target({FIELD, PARAMETER}) 是对用户使用的说明;它不允许 @Blue 被用于方法、类型、局部变量和其他标注。
- @BindingAnnotation 是 Guice 特定的信号,表示你希望该标注被用于绑定标注。当用户将多于一个的绑定标注应用于同一个可注入元素时,Guice 会报错。
有属性的标注
你也可以绑定到标注实例,即,你可以有多个绑定指向同样的类型和标注类型,但每个绑定拥有不同的标注属性值。如果 Guice 找不到拥有特定属性值的标注实例,它会去找一个绑定到该标注类型的绑定。
例如,我们有一个绑定标注 @Named,它有一个字符属性值。
@Retention(RUNTIME)
@Target({ FIELD, PARAMETER })
@BindingAnnotation
public @interface Named {
String value();
}
class NamedAnnotation implements Named {现在我们可以使用这个标注实现来创建一个指向 @Named 的绑定。
final String value;
public NamedAnnotation(String value) {
this.value = value;
}
public String value() {
return this.value;
}
public int hashCode() {
// This is specified in java.lang.Annotation.
return 127 * "value".hashCode() ^ value.hashCode();
}
public boolean equals(Object o) {
if (!(o instanceof Named))
return false;
Named other = (Named) o;
return value.equals(other.value());
}
public String toString() {
return "@" + Named.class.getName() + "(value=" + value + ")";
}
public Class<? extends Annotation> annotationType() {
return Named.class;
}
}
.annotatedWith(new NamedAnnotation("Bob"))
.to(Bob.class);
因为通过名字标记一个绑定非常普遍,以至于 Guice 在 com.google.inject.name 中提供了一个十分有用的 @Named 的实现。
隐式绑定
@Inject
Mixer(Concrete concrete) {
...
}
}
注入提供者
void injectAtm(Provider<Money> atm) {
Money one = atm.get();
Money two = atm.get();
...
}
正如你所看到的那样, Provider 接口简单得不能再简单了,它不会为简单的单元测试添加任何麻烦。
注入常数值
- 基本类型(int, char, ...)
- 基本 wrapper 类型(Integer, Character, ...)
- Strings
- Enums
- Classes
首先,当绑定到这些类型的常数值的时候,你不需要指定你要绑定到的类型。Guice 可以根据值判断类型。例如,一个绑定标注名为 TheAnswer:
转换字符串
如果 Guice 仍然无法找到一个上述类型的显式绑定,它会去找一个拥有相同绑定标识的常量 String 绑定,并试图将字符串转换到相应的值。例如:
定制的提供者
final Service service;
@Inject
WidgetProvider(Service service) {
this.service = service;
}
public Widget get() {
return new Widget(service);
}
}
示例:与 JNDI 集成
import com.google.inject.*;
import javax.naming.*;
class JndiProvider<T> implements Provider<T> {
@Inject Context context;
final String name;
final Class<T> type;
JndiProvider(Class<T> type, String name) {
this.name = name;
this.type = type;
}
public T get() {
try {
return type.cast(context.lookup(name));
}
catch (NamingException e) {
throw new RuntimeException(e);
}
}
/**
* Creates a JNDI provider for the given
* type and name.
*/
static <T> Provider<T> fromJndi(
Class<T> type, String name) {
return new JndiProvider<T>(type, name);
}
}
我们可以使用定制的 JndiProvider 来将 DataSource 绑定到来自 JNDI 的一个对象:
import static mypackage.JndiProvider.fromJndi;
import javax.naming.*;
import javax.sql.DataSource;
...
// Bind Context to the default InitialContext.
bind(Context.class).to(InitialContext.class);
// Bind to DataSource from JNDI.
bind(DataSource.class)
.toProvider(fromJndi(DataSource.class, "..."));
限制绑定的作用域
class MySingleton {
...
}
可以使用 Binder.bindScope() 为定制的作用域指定标注。例如,对于标注 @SessionScoped 和一个 Scope 的实现 ServletScopes.SESSION:
创建作用域标注
- 有一个 @Retention(RUNTIME) 标注,从而使我们可以在运行时看到该标注。
- 有一个 @Target({TYPE}) 标注。作用域标注只用于实现类。
- 有一个 @ScopeAnnotation 元标注。一个类只能使用一个此类标注。
例如:
* Scopes bindings to the current transaction.
*/
@Retention(RUNTIME)
@Target({TYPE})
@ScopeAnnotation
public @interface TransactionScoped {}
尽早加载绑定
在不同作用域间注入
开发阶段
在开发阶段,Guice 会在需要时加载单件对象。这样,你的应用可以快速启动,只加载你正在测试的部分。
在产品阶段,Guice 会在启动时加载全部单件对象。这帮助你尽早捕获错误,提前优化性能。
你的模块也可以使用方法拦截和其他基于当前阶段的绑定。例如,一个拦截器可能会在开发阶段检查你是否在作用域之外使用对象。
拦截方法
...
binder.bindInterceptor(
any(), // Match classes.
annotatedWith(Transactional.class), // Match methods.
new TransactionInterceptor() // The interceptor.
);
尽量让匹配代码多做些过滤工作,而不是在拦截器中过滤。因为匹配代码只在启动时运行一次。
静态注入
我们发现更实用的解决方案是使用静态注入:
class User {
@Inject
static AuthorizationService authorizationService;
...
}
可选注入
可选注入只能应用于字段和方法,而不能用于构造器。对于方法,如果一个参数的绑定找不到,Guice 就不会注入该方法,即便其他参数的绑定是可用的。
绑定到字符串
...
bind(named("bob")).to(10);
Struts 2支持
一个计数器的例子
public class Counter {
int count = 0;
/** Increments the count and returns the new value. */
public synchronized int increment() {
return count++;
}
}
final Counter counter;
@Inject
public Count(Counter counter) {
this.counter = counter;
}
public String execute() {
return SUCCESS;
}
public int getCount() {
return counter.increment();
}
}
class="mypackage.Count">
<result>/WEB-INF/Counter.jsp</result>
</action>
<html>
<body>
<h1>Counter Example</h1>
发表评论
-
J2ME 控制台中文输出乱码问题解决
2010-05-05 18:34 1496J2ME 控制台中文输出乱码问题解决 修改wtk路径\wtk ... -
spring mvc 2.5 jsp el表达式 不起作用的解决方案
2008-06-11 11:59 4931最近正在学习spring mvc 2.5,发现基于annota ... -
java泛型通过反射得到class
2007-04-20 15:49 5850Class<T> entityClass = (C ... -
[转]Don’t repeat the DAO!
2007-04-20 15:46 1555译者:Nicholas @ Nirvana St ... -
java合成jpeg图像 压缩问题 resize问题
2007-01-09 16:44 7842在网上找到的java输出图像的例子里面一般都没有对jpeg图像 ... -
转 FreeMarker一篇通
2006-12-28 09:53 3543大家看文章标题就应该知道,我想用一篇文章,把大家从对fr ... -
java.lang.OutOfMemoryError: Java heap space 解决方法
2006-12-18 14:40 29219windows 更改系统环境变量 加上JAVA_OPTS=-X ... -
转 JAVA中的指针,引用及对象的clone
2006-12-11 13:40 1523看到这个标题,是不是 ... -
转载 FreeMarker vs. Velocity
2006-12-05 10:20 22111、概述<o:p></o:p> l ...
相关推荐
### Java on Guice:深入理解依赖注入 #### 核心概念与价值 “Java on Guice”这篇文档探讨了依赖注入(Dependency Injection, DI)在Java应用中的实践方式及其带来的诸多好处。它由Google的Bob Lee和Kevin ...
4. **注解驱动**:Guice使用Java的注解(如`@Inject`、`@Singleton`等)来标记和配置依赖。`@Inject`用于声明依赖,`@Singleton`表示单例模式,确保一个类型在整个应用程序中只有一个实例。 5. **测试**:在Guice中...
Java Guice 3.0是一款轻量级的依赖注入(Dependency Injection, DI)框架,它致力于简化Java应用程序的构建过程,让组件之间的依赖关系更加清晰和易于管理。依赖注入是一种设计模式,它允许代码在运行时自动管理和...
2. **注解驱动**:Guice使用Java的注解来标记接口和实现,如`@Inject`用于标记构造函数、字段或方法,表示需要依赖注入。`@Singleton`表示该对象为单例,`@Named`用于指定特定的依赖实现。 3. **模块(Modules)**...
### Java Guice 依赖注入教程 #### 概述 本文档详细介绍如何利用 Google 开源的依赖注入框架 Guice 进行 Java 开发。通过本文档的学习,开发者将能够更好地理解 Guice 的工作原理及其在实际项目中的应用。文档涵盖...
5. **注解处理(Annotation Processing)**:Guice使用Java的注解处理工具(APT)在编译时生成元数据,从而在运行时提高效率。这包括生成代理类和绑定类。 6. **AOP(面向切面编程)支持**:虽然Guice不是专门的AOP...
总的来说,Guice2以其精巧的API和对依赖注入的深入理解,为Java开发者提供了一个高效且易于使用的框架,帮助他们构建更加模块化、可测试和易于维护的代码。在实际项目中,结合Guice2和其他Java框架(如Spring)的...
guice.jar guice.jar guice.jar guice.jar guice.jar guice.jar guice.jar
Guice是一个轻量级的,开源的,依赖项注入框架,适用于Java 6及更高版本。 这样做是为了减轻工厂的需要并在Java代码中使用new。 在某些情况下,您仍然需要像往常一样编写工厂,但是使用Guice,您的代码将不再直接...
**基于Guice的简单项目** 在Java开发中,依赖注入(Dependency Injection,简称DI)是一种设计...通过学习和实践这个项目,开发者可以更好地掌握Guice的工作原理和使用技巧,为未来的Java项目带来更高效的代码结构。
- **强大的注解支持**:Guice对Java标准注解和自定义注解的广泛支持,使代码更具表达力。 - **模块化**:Guice的模块化设计方便了组件的隔离和组合,便于构建复杂系统。 5. **使用场景**: - 小型项目或模块,不...
Guice是一个专门为Java 5及后续版本设计的超轻量级依赖注入框架。它旨在简化应用程序组件之间的依赖管理,并提供了一个更为简洁、灵活的方式来处理对象间的耦合关系。Guice的设计目标是使开发人员能够更轻松地创建可...
例如,在上面的例子中,我们可以使用 Guice 来注入 Service 对象到 Client 对象中,而不需要使用工厂模式或其他方式来获取 Service 对象。这可以使得 Client 对象更加灵活和可维护。 Guice 的优越性体现在以下几个...
1. **注解驱动的配置**:Guice使用Java注解(如`@Inject`,`@Singleton`等)来声明依赖关系和组件生命周期。例如,`@Inject`注解用于标记一个字段或方法作为依赖注入的入口。 2. **模块(Module)**:Guice模块定义...
Guice 是 Google 推出的一款轻量级的依赖注入框架,专为 Java 5 及其后续版本设计。依赖注入(Dependency Injection,简称 DI)是一种设计模式,它允许开发者在不直接创建对象的情况下,将依赖关系从代码中解耦出来...
Google Guice作为一款100%纯Java实现的依赖注入框架,以其简单性、高性能和易于使用的特性而受到广泛好评。本书《Google Guice: Agile Lightweight Dependency Injection Framework》旨在深入探讨Guice的核心概念和...
**Google Guice** 是一个轻量级的 Java 依赖注入容器,它为 Java 5 及以上版本提供支持。与传统的对象创建方式相比,Guice 通过减少样板代码(boilerplate code)来提高开发效率,使得单元测试更为简便,并增强了...
标签:google、inject、guice、中文文档、jar包、java; 使用方法:解压翻译后的API文档,用浏览器打开“index.html”文件,即可纵览文档内容。 人性化翻译,文档中的代码和结构保持不变,注释和说明精准翻译,请...
通过使用官方提供的 Guice 模块,开发者可以轻松地配置和管理 Shiro 的各种组件,同时利用 Guice 强大的依赖注入功能。此外,对于需要 Web 支持或 AOP 注解的应用程序来说,Shiro 提供了额外的模块来满足这些需求。...