一、Setter注入-Spring
Setter注入是指外部程序通过调用setter方法为客户端注入所依赖的对象。Spring就有采用这种方式。
publicclassClient{ privateService1service1; publicvoidsetService1(Service1service1){ this.service1=service1; } publicvoiddoSomething(){ service1.execute(); } publicstaticvoidmain(String[]args){ ApplicationContextctx=newClassPathXmlApplicationContext("pattern/part2/chapter6/setter/spring-beans.xml"); Clientclient=(Client)ctx.getBean("client"); client.doSomething(); } }
<beanid="client"class="com.persia.client"> <propertyname="service1"ref="service1"/> </bean>
mock
publicclassClientTestDrive{ publicvoidtestDoSomething(){ Clientclient=newClient(); MockService1mockService1=newMockService1(); client.setService1(mockService1); client.doSomething(); assertEquals(mockService1.isExecuted(),true,"TestFailed"); } publicstaticvoidmain(String[]args){ newClientTestDrive().testDoSomething(); } privatestaticvoidassertEquals(booleanactual,booleanexpected,Stringmessage){ if(actual!=expected)thrownewIllegalArgumentException(message); } privatestaticclassMockService1implementsService1{ privatebooleanexecuted; publicbooleanisExecuted(){ returnexecuted; } @Override publicvoidexecute(){ executed=true; } } }
二、Constructor注入-PicoContainer
Constructor注入就是通过带参数的构造器注入依赖对象。PicoContainer就是采用这种方式注入的。
publicclassClient{ privateService1service1; publicClient(Service1service1){ this.service1=service1; } publicvoiddoSomething(){ service1.execute(); } privatestaticMutablePicoContainerconfigure(){ MutablePicoContainerpico=newDefaultPicoContainer(); pico.addComponent(Service1Impl.class);//registeranimplementationofService1 pico.addComponent(Service2Impl.class); pico.addComponent(Client.class); returnpico; } publicstaticvoidmain(String[]args){ MutablePicoContainerpico=configure(); Clientclient=pico.getComponent(Client.class); client.doSomething(); } }
mock
publicclassClientTestDrive{ publicvoidtestDoSomething(){ MockService1mockService1=newMockService1(); Clientclient=newClient(mockService1); client.doSomething(); assertEquals(mockService1.isExecuted(),Stringmessage){ if(actual!=expected)thrownewIllegalArgumentException(message); } privatestaticclassMockService1implementsService1{ privatebooleanexecuted; publicbooleanisExecuted(){ returnexecuted; } @Override publicvoidexecute(){ executed=true; } } }
三、Annotation注入-Guice
只要接口是@Retention(RUNTIME)的,就可以在运行时期通过反射读取。Annotation注入的方式就是把实例化信息和对象之间的依赖关系信息使用注解的形式表达,只要在运行时期得到它们,就知道如何初始化服务对象了。Guice就是采用这种方式的。注解可以注解在方法、构造器、属性上面。
@ImplementedBy(Service1Impl.class) publicinterfaceService1{ voidexecute(); } publicclassClient{ privateService1service1; @Inject publicClient(Service1service1){ this.service1=service1; } publicvoiddoSomething(){ service1.execute(); } publicstaticvoidmain(String[]args){ Injectorinjector=Guice.createInjector(); Clientclient=injector.getInstance(Client.class); client.doSomething(); } }
mock
publicclassClientTestDrive{ publicvoidtestDoSomething(){ MockService1mockService1=newMockService1(); Clientclient=newClient(mockService1); client.doSomething(); assertEquals(mockService1.isExecuted(),Stringmessage){ if(actual!=expected)thrownewIllegalArgumentException(message); } privatestaticclassMockService1implementsService1{ privatebooleanexecuted; publicbooleanisExecuted(){ returnexecuted; } @Override publicvoidexecute(){ executed=true; } } }
四、Interface注入-Avalon
客户端通过实现容器/框架所规范的某些特殊接口,在返回这些依赖对象之前,容器回调这些接口方法,注入依赖的服务对象。Apache的Avalon就是采用这种方式注入的。
publicinterfaceServiceAware{ voidinjectService(Serviceservice); } publicclassClientimplementsServiceAware{ privateServiceservice; @Override publicvoidinjectService(Serviceservice){ this.service=service; } publicvoiddoSomething(){ service.execute(); } publicstaticvoidmain(String[]args){ InterfaceInjector.configure(); Clientclient=InterfaceInjector.getInstance(Client.class); client.doSomething(); } }
接口注入
publicclassInterfaceInjector{ privatestaticInterfaceInjectorinjector; privateMap<Class,Object>services=newHashMap<Class,Object>(); publicstaticvoidconfigure(){ load(newInterfaceInjector()); } publicstatic<T>TgetInstance(Class<T>clazz){ returninjector.loadService(clazz); } privatestaticvoidload(InterfaceInjectorcontainer){ InterfaceInjector.injector=container; } @SuppressWarnings("unchecked") private<T>TloadService(Class<T>clazz){ Objectservice=injector.services.get(clazz); if(service!=null){ return(T)service; } try{ service=clazz.newInstance(); if(serviceinstanceofServiceAware){ ((ServiceAware)service).injectService(newServiceImpl()); } injector.services.put(clazz,service); }catch(InstantiationExceptione){ e.printStackTrace(); }catch(IllegalAccessExceptione){ e.printStackTrace(); } return(T)service; } }
mock
publicclassClientTestDrive{ publicvoidtestDoSomething(){ MockServicemockService=newMockService(); Clientclient=newClient(); client.injectService(mockService); client.doSomething(); assertEquals(mockService.isExecuted(),Stringmessage){ if(actual!=expected)thrownewIllegalArgumentException(message); } privatestaticclassMockServiceimplementsService{ privatebooleanexecuted; publicbooleanisExecuted(){ returnexecuted; } @Override publicvoidexecute(){ executed=true; } } }
五、Parameter注入
外部程序通过函数参数,给客户端注入所依赖的服务对象。构造器注入和setter注入都可以看成是参数注入的变形。
publicclassClient{ publicvoiddoSomething(Serviceservice){ service.execute(); } publicstaticvoidmain(String[]args){ //createaservice Serviceservice=newServiceImpl(); //putnew-createdserviceinstancebyinputparameter newClient().doSomething(service); } }
mock
publicclassClientTestDrive{ publicvoidtestDoSomething(){ MockServicemockService=newMockService(); Clientclient=newClient(); client.doSomething(mockService); assertEquals(mockService.isExecuted(),Stringmessage){ if(actual!=expected)thrownewIllegalArgumentException(message); } privatestaticclassMockServiceimplementsService{ privatebooleanexecuted; publicbooleanisExecuted(){ returnexecuted; } @Override publicvoidexecute(){ executed=true; } } }
六、其他形式的注入
诸如Guice使用的Providers的注入方式等。
小结:依赖注入的好处:把实例化的过程交给框架/容器来处理,这样可以更专注于业务逻辑的开发。Setter和Contructor注入是常见的注入方式。
摘自《漫谈设计模式》