一、Setter注入-Spring
Setter注入是指外部程序通过调用setter方法为客户端注入所依赖的对象。Spring就有采用这种方式。
public class Client {
private Service1 service1;
public void setService1(Service1 service1) {
this.service1 = service1;
}
public void doSomething() {
service1.execute();
}
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("pattern/part2/chapter6/setter/spring-beans.xml");
Client client = (Client) ctx.getBean("client");
client.doSomething();
}
}
配置文件
<bean id="client" class="com.persia.client">
<property name="service1" ref="service1"/>
</bean>
mock
public class ClientTestDrive {
public void testDoSomething() {
Client client = new Client();
MockService1 mockService1 = new MockService1();
client.setService1(mockService1);
client.doSomething();
assertEquals(mockService1.isExecuted(), true, "Test failed");
}
public static void main(String[] args) {
new ClientTestDrive().testDoSomething();
}
private static void assertEquals(boolean actual, boolean expected, String message) {
if (actual != expected) throw new IllegalArgumentException(message);
}
private static class MockService1 implements Service1 {
private boolean executed;
public boolean isExecuted() {
return executed;
}
@Override
public void execute() {
executed = true;
}
}
}
二、Constructor注入-PicoContainer
Constructor注入就是通过带参数的构造器注入依赖对象。PicoContainer就是采用这种方式注入的。
public class Client {
private Service1 service1;
public Client(Service1 service1) {
this.service1 = service1;
}
public void doSomething() {
service1.execute();
}
private static MutablePicoContainer configure() {
MutablePicoContainer pico = new DefaultPicoContainer();
pico.addComponent(Service1Impl.class);//register an implementation of Service1
pico.addComponent(Service2Impl.class);
pico.addComponent(Client.class);
return pico;
}
public static void main(String[] args) {
MutablePicoContainer pico = configure();
Client client = pico.getComponent(Client.class);
client.doSomething();
}
}
mock
public class ClientTestDrive {
public void testDoSomething() {
MockService1 mockService1 = new MockService1();
Client client = new Client(mockService1);
client.doSomething();
assertEquals(mockService1.isExecuted(), true, "Test failed");
}
public static void main(String[] args) {
new ClientTestDrive().testDoSomething();
}
private static void assertEquals(boolean actual, boolean expected, String message) {
if (actual != expected) throw new IllegalArgumentException(message);
}
private static class MockService1 implements Service1 {
private boolean executed;
public boolean isExecuted() {
return executed;
}
@Override
public void execute() {
executed = true;
}
}
}
三、Annotation注入-Guice
只要接口是@Retention(RUNTIME)的,就可以在运行时期通过反射读取。Annotation注入的方式就是把实例化信息和对象之间的依赖关系信息使用注解的形式表达,只要在运行时期得到它们,就知道如何初始化服务对象了。Guice就是采用这种方式的。注解可以注解在方法、构造器、属性上面。
@ImplementedBy(Service1Impl.class)
public interface Service1 {
void execute();
}
public class Client {
private Service1 service1;
@Inject
public Client(Service1 service1) {
this.service1 = service1;
}
public void doSomething() {
service1.execute();
}
public static void main(String[] args) {
Injector injector = Guice.createInjector();
Client client = injector.getInstance(Client.class);
client.doSomething();
}
}
mock
public class ClientTestDrive {
public void testDoSomething() {
MockService1 mockService1 = new MockService1();
Client client = new Client(mockService1);
client.doSomething();
assertEquals(mockService1.isExecuted(), true, "Test failed");
}
public static void main(String[] args) {
new ClientTestDrive().testDoSomething();
}
private static void assertEquals(boolean actual, boolean expected, String message) {
if (actual != expected) throw new IllegalArgumentException(message);
}
private static class MockService1 implements Service1 {
private boolean executed;
public boolean isExecuted() {
return executed;
}
@Override
public void execute() {
executed = true;
}
}
}
四、Interface注入-Avalon
客户端通过实现容器/框架所规范的某些特殊接口,在返回这些依赖对象之前,容器回调这些接口方法,注入依赖的服务对象。Apache的Avalon就是采用这种方式注入的。
public interface ServiceAware {
void injectService(Service service);
}
public class Client implements ServiceAware {
private Service service;
@Override
public void injectService(Service service) {
this.service = service;
}
public void doSomething() {
service.execute();
}
public static void main(String[] args) {
InterfaceInjector.configure();
Client client = InterfaceInjector.getInstance(Client.class);
client.doSomething();
}
}
接口注入
public class InterfaceInjector {
private static InterfaceInjector injector;
private Map<Class, Object> services = new HashMap<Class, Object>();
public static void configure() {
load(new InterfaceInjector());
}
public static <T> T getInstance(Class<T> clazz) {
return injector.loadService(clazz);
}
private static void load(InterfaceInjector container) {
InterfaceInjector.injector = container;
}
@SuppressWarnings("unchecked")
private <T> T loadService(Class<T> clazz) {
Object service = injector.services.get(clazz);
if (service != null) {
return (T) service;
}
try {
service = clazz.newInstance();
if (service instanceof ServiceAware) {
((ServiceAware) service).injectService(new ServiceImpl());
}
injector.services.put(clazz, service);
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return (T) service;
}
}
mock
public class ClientTestDrive {
public void testDoSomething() {
MockService mockService = new MockService();
Client client = new Client();
client.injectService(mockService);
client.doSomething();
assertEquals(mockService.isExecuted(), true, "Test failed");
}
public static void main(String[] args) {
new ClientTestDrive().testDoSomething();
}
private static void assertEquals(boolean actual, boolean expected, String message) {
if (actual != expected) throw new IllegalArgumentException(message);
}
private static class MockService implements Service {
private boolean executed;
public boolean isExecuted() {
return executed;
}
@Override
public void execute() {
executed = true;
}
}
}
五、Parameter注入
外部程序通过函数参数,给客户端注入所依赖的服务对象。构造器注入和setter注入都可以看成是参数注入的变形。
public class Client {
public void doSomething(Service service) {
service.execute();
}
public static void main(String[] args) {
//create a service
Service service = new ServiceImpl();
//put new-created service instance by input parameter
new Client().doSomething(service);
}
}
mock
public class ClientTestDrive {
public void testDoSomething() {
MockService mockService = new MockService();
Client client = new Client();
client.doSomething(mockService);
assertEquals(mockService.isExecuted(), true, "Test failed");
}
public static void main(String[] args) {
new ClientTestDrive().testDoSomething();
}
private static void assertEquals(boolean actual, boolean expected, String message) {
if (actual != expected) throw new IllegalArgumentException(message);
}
private static class MockService implements Service {
private boolean executed;
public boolean isExecuted() {
return executed;
}
@Override
public void execute() {
executed = true;
}
}
}
六、其他形式的注入
诸如Guice使用的Providers的注入方式等。
小结:依赖注入的好处:把实例化的过程交给框架/容器来处理,这样可以更专注于业务逻辑的开发。Setter和Contructor注入是常见的注入方式。
摘自《漫谈设计模式》