这篇是承接《轻量级 Java 开发框架 设计》系列Blog文的后续文章。本文是最新 《Hasor 开发指南》中依赖注入章节的完整内容,开发指南目前仍然在努力编写中。
概念
“依赖注入(DI)”有时候也被称为“控制反转(IoC)”本质上它们是同一个概念。具体是指,当某个类调用另外一个类的时候通常需要调用者来创建被调用者。但在控制反转的情况下调用者不在主动创建被调用者,而是改为由容器注入,因此而得名。
这里的“创建”强调的是调用者的主动性。而依赖注入则不在需要调用者主动创建被调用者。
举个例子通常情况下调用者(ClassA),会先创建好被调用者(FunBean),然后在调用方法callFoo中调用被调用者(FunBean)的foo方法:
public class ClassA { private FunBean funBean = new FunBean(); public void callFoo() { this.funBean.foo(); } } public class FunBean { public void foo() { System.out.println("say ..."); } }
使用了依赖注入的情况恰恰相反,调用者(ClassA)事先并不知道要创建哪个被调用者(FunBean)。ClassA调用的是被注入进来的FunBean,通常我们会为需要依赖注入的对象留有set方法,在调用callFoo方法之前是需要先将funBean对象通过setFunBean方法设置进来的。例如:
public class ClassA { private FunBean funBean = null; public void setFunBean(FunBean funBean) { this.funBean = funBean; } public void callFoo() { this.funBean.foo(); } } public class FunBean { ……
传统注入方式
严格意义上来说注入的形式分为两种,它们是“构造方法注入”和“set属性注入”。我们经常听到有第三种注入方式叫“接口注入”。其实它只是“set属性注入”的一种接口表现形式。
public class ClassA { private FunBean funBean = null; public ClassA(FunBean funBean) { this.funBean = funBean; } public void callFoo() { this.funBean.foo(); } }
public class ClassA { private FunBean funBean = null; public void setFunBean(FunBean funBean) { this.funBean = funBean; } public void callFoo() { this.funBean.foo(); } }
public interface IClassA { public void setFunBean(FunBean funBean); } public class ClassA implements IClassA{ private FunBean funBean = null; public void setFunBean(FunBean funBean) { this.funBean = funBean; } public void callFoo() { this.funBean.foo(); } }
Guice与JSR-330
JSR-330相关的API是由“javax.inject.*”软件包提供的一组标准API。通过注解作为其表现形式。Hasor使用Google旗下的开源DI容器Guice作为其JSR-330的标准支持组件。
Guice是Google开发的一个轻量级,基于Java5(主要运用泛型与注释特性)的依赖注入框架(IoC)。Guice非常小而且快。Guice是类型安全的,它能够对构造函数,属性,方法(包含任意个参数的任意方法,而不仅仅是setter方法)进行注入。
Guice还具有一些可选的特性比如:自定义scopes,传递依赖,静态属性注入,与Spring集成和AOP联盟方法注入等。对于DI框架来说,性能是很重要的,Guice比Spring快这是主流说法,在Guice的官方网站上您可以看到它宣称比Spring快1000倍!
Hasor选用Guice是由于它的开发接口十分灵活,比起Spring而言Guice更适合作为一个内嵌DI工具来使用。
由于Guice是JSR-330标准的实现,这也就使得Hasor也具备了支持JSR-330标准的能力。在下面几个小节会讲解如何使用JSR-330标准将其注入到需要的类上。
我们先假定有一个被调用者(PojoBean),下面是PojoBean类的源代码:
public class PojoBean { private String uuid = UUID.randomUUID().toString(); private String name = "马三"; private String address = "北京马连洼街道办..."; public String getUuid() { return uuid; } public void setUuid(String uuid) { this.uuid = uuid; } …… }
构造方法注入
public class ConstructorInject { private PojoBean userBean; @javax.inject.Inject/*依赖注入*/ public ConstructorInject(PojoBean userBean) { this.userBean = userBean; } public String getUserName() { return this.userBean.getName(); } }
属性方式注入
public class MethodInject { private PojoBean userBean; @javax.inject.Inject/*依赖注入*/ public void setUserBean(PojoBean userBean) { this.userBean = userBean; } public String getUserName() { return this.userBean.getName(); } }
字段方式注入
字段注入是DI容器对“set属性注入”的一种改进.这种改进使得被注入的对象不在需要实现一个set方法,DI容器会主动的将要注入的对象赋值到给定的字段上。
public class FieldInject { @javax.inject.Inject/*依赖注入*/ private PojoBean userBean; public String getUserName() { return this.userBean.getName(); } }
单例Bean
单例,通常是指整个应用程序范围内某个类型对象只有一个。Hasor使用AppContext接口表示一个应用程序,在一个AppContext内Hasor通过下面这样的代码可以保证单例:
@javax.inject.Singleton/*声明单例*/ public class SingletonBean { private long time = 0; public SingletonBean() { time = System.currentTimeMillis(); } public void foo() { System.out.println("create at time:" + time); } }