Spring控制反转和依赖注入
控制反转的类型
控制反转(IOC)旨在提供一种更简单的机制,来设置组件的依赖项,并在整个生命周期管理这些依赖项。通常,控制反转可以分成两种子类型:依赖注入(DI)和依赖查找(DL),这些子类型各自又可以被进一步分解为IOC服务的具体实现1。依赖查找1。1依赖拉取
依赖拉取(DependencyPull),即根据需要,从注册表中提取依赖项,以下代码显示了基于Spring的依赖拉取publicclassDependencyPull{publicstaticvoidmain(String〔〕args){ClassPathXmlApplicationContextctxnewClassPathXmlApplicationContext(springappcontext。xml);ctx。getBean(renderer,MessageRenderer。class);}}1。2上下文依赖查找
上下文依赖查找(contextualizeddependencylookup,CDL),同样属于依赖查找的子类型,和依赖拉取有点类似,但在CDL中,查找是针对管理资源的容器执行的,这个容器通常由应用程序服务器或框架(Tomcat、JBoss、Spring)提供,比如以下代码显示了一个提供依赖查找服务的容器接口publicinterfaceContainer{根据key获取相应的依赖项ObjectgetDependency(Stringkey);}
CDL通过让组件实现以下代码接口来进行工作publicinterfaceManagedComponent{voidperformLookup(Containercontainer);}
组件需要实现该接口,当容器准备好将依赖项传递给组件时,会依次调用每个组件的performLookup()方法,然后组件就可以使用Container接口查找所需的依赖项publicclassContextualizedDependencyLookupimplementsManagedComponent{privateDependencydependency;OverridepublicvoidperformLookup(Containercontainer){this。dependency(Dependency)container。getDependency(myDependency);}OverridepublicStringtoString(){returndependency。toString();}}2。依赖注入2。1构造函数注入
当在组件的构造函数中提供依赖项时,就会发生构造函数依赖注入publicclassConstructorInjection{privateDependencydependency;publicConstructorInjection(Dependencydependency){this。dependencydependency;}OverridepublicStringtoString(){returndependency。toString();}}2。2setter函数注入
Ioc容器通过JavaBean样式的setter方法注入组件的依赖项publicclassSetterInjection{privateDependencydependency;publicvoidsetDependency(Dependencydependency){this。dependencydependency;}OverridepublicStringtoString(){returndependency。toString();}}
在Spring中,还支持另一种被称为字段注入(fieldinjection)的注入类型,在后面学习使用Autowire注解进行自动装配时将介绍该注入类型Spring中的控制反转1。Bean和BeanFactory
Spring的依赖注入容器的核心是BeanFactory,它负责管理组件,包括依赖项以及它们的生命周期。如果我们想获得一个组件(Bean),就必须创建一个实现了BeanFactory接口的实例,并对其进行配置
虽然BeanFactory可以通过编程方式配置,但更常见的做法是使用某种配置文件在外部对其进行配置。Bean配置可以由实现BeanDefinition接口的类的实例来表示,对于任何实现了BeanDefinitionReader接口的BeanFactory实现类来说,都可以使用PropertiesBeanDefinitionReader或XmlBeanDefinitionReader从配置文件读取BeanDefinition数据
定义一组接口:publicinterfaceOracle{Stringoutput();}publicclassOracleImplimplementsOracle{OverridepublicStringoutput(){returnhelloworld;}}
接下来我们来看一看,Spring的BeanFactory如何被初始化并用于获取Bean实例publicclassXmlConfigWithBeanFactory{publicstaticvoidmain(String〔〕args){DefaultListableBeanFactory是Spring提供的两个主要BeanFactory实现之一DefaultListableBeanFactoryfactorynewDefaultListableBeanFactory();XmlBeanDefinitionReaderrdrnewXmlBeanDefinitionReader(factory);使用XmlBeanDefinitionReader从XML文件读取BeanDefinition信息rdr。loadBeanDefinitions(newClassPathResource(springxmlbeanfactoryconfig。xml));使用在XML配置文件中配置的名称oracle来获取beanOracleoracle(Oracle)factory。getBean(oracle);System。out。println(oracle。getInfo());}}lt;?xmlversion1。0encodingUTF8?beansxmlnshttp:www。springframework。orgschemabeansxmlns:xsihttp:www。w3。org2001XMLSchemainstancexsi:schemaLocationhttp:www。springframework。orgschemabeanshttp:www。springframework。orgschemabeansspringbeans。xsdbeanidoraclenameoracleclasscom。example。OracleImplbeans
ApplicationContext接口是BeanFactory的一个扩展,除了DI服务外,还提供其他如事务和AOP等服务。在开发基于Spring的应用程序时,建议通过ApplicationContext接口与Spring交互2。设置Spring配置2。1XML配置
对于XML配置,需要声明应用程序需要的由Spring提供的名称空间基础信息,下面所示配置仅声明用于定义bean的名称空间lt;?xmlversion1。0encodingUTF8?beansxmlnshttp:www。springframework。orgschemabeansxmlns:xsihttp:www。w3。org2001XMLSchemainstancexmlns:phttp:www。springframework。orgschemapxsi:schemaLocationhttp:www。springframework。orgschemabeanshttp:www。springframework。orgschemabeansspringbeans。xsdbeanidproviderclasscom。example。HelloWorldMessageProviderbeanidrenderclasscom。example。StandardOutMessageRenderp:messageProviderrefproviderbeans2。2注解配置
要想在应用程序使用Spring的注解支持,需要在XML配置中声明lt;?xmlversion1。0encodingUTF8?beansxmlnshttp:www。springframework。orgschemabeansxmlns:xsihttp:www。w3。org2001XMLSchemainstancexmlns:contexthttp:www。springframework。orgschemacontextxsi:schemaLocationhttp:www。springframework。orgschemabeanshttp:www。springframework。orgschemabeansspringbeans。xsdhttp:www。springframework。orgschemacontexthttp:www。springframework。orgschemacontextspringcontext。xsdcontext:componentscanbasepackagecom。examplebeans
标记告诉Spring扫描代码,从而找到Component等注解注入的bean,以及支持在指定包(及其所有子包)下使用Autowire等注解的bean2。3Java配置
配置类使用Configuration注解,并包含用Bean注解的方法,这些方法由IOC容器直接调用来实例化bean,bean名称与用于创建它的方法的名称相同ConfigurationpublicclassHelloWorldConfiguration{BeanpublicMessageProviderprovider(){returnnewHelloWorldMessageProvider();}BeanpublicMessageRenderrender(){StandardOutMessageRenderrendernewStandardOutMessageRender();render。setMessageProvider(provider());returnrender;}}
如果想从该类中读取配置信息,需要一个不同的ApplicationContext实现publicclassHelloWorldSpringAnnotated{publicstaticvoidmain(String〔〕args){AnnotationConfigApplicationContextctxnewAnnotationConfigApplicationContext(HelloWorldConfiguration。class);MessageRenderrenderctx。getBean(render,MessageRender。class);render。render();}}3。setter注入
使用XML配置来配置setter注入,需要在标记下指定标记,为其注入一个依赖项beansxmlnshttp:www。springframework。orgschemabeansxmlns:xsihttp:www。w3。org2001XMLSchemainstancexmlns:phttp:www。springframework。orgschemapxsi:schemaLocationhttp:www。springframework。orgschemabeanshttp:www。springframework。orgschemabeansspringbeans。xsdbeanidproviderclasscom。example。HelloWorldMessageProviderbeanidrenderclasscom。example。StandardOutMessageRenderpropertynamemessageProviderrefproviderbeanbeans
如果使用注解,只需要向setter方法添加一个Autowired注解Service(render)publicclassStandardOutMessageRenderimplementsMessageRender{。。。OverrideAutowiredpublicvoidsetMessageProvider(MessageProvidermessageProvider){this。messageProvidermessageProvider;}}4。构造函数注入publicclassConfigurableMessageProviderimplementsMessageProvider{privateStringmessage;publicConfigurableMessageProvider(Stringmessage){this。messagemessage;}OverridepublicStringgetMessage(){returnnull;}}
使用XML方式注入lt;?xmlversion1。0encodingUTF8?beansxmlnshttp:www。springframework。orgschemabeansxmlns:xsihttp:www。w3。org2001XMLSchemainstancexmlns:chttp:www。springframework。orgschemacxsi:schemaLocationhttp:www。springframework。orgschemabeanshttp:www。springframework。orgschemabeansspringbeans。xsdbeanidoraclenameoracleclasscom。example。OracleImpl!使用constructorarg标记beanidmessageProviderclasscom。example。ConfigurableMessageProviderconstructorargvaluehelloworldbean!使用c名称空间beanidproviderclasscom。example。ConfigurableMessageProviderc:messagehelloworldbeans
使用注解方式ServicepublicclassConfigurableMessageProviderimplementsMessageProvider{privateStringmessage;AutowiredpublicConfigurableMessageProvider(Value(helloworld)Stringmessage){this。messagemessage;}OverridepublicStringgetMessage(){returnnull;}}