1.4.2。依赖性和具体配置

如上一节所述,您能够将bean属性和结构函数参数定义为对其余托管bean(协作者)的援用或内联定义的值。Spring的基于XML的配置元数据为此目标在其<property/>和<constructor-arg/>元素中反对子元素类型。

直值(原语,字符串等)
在value所述的属性<property/>元素指定属性或结构器参数的人类可读的字符串示意。Spring的 转换服务用于将这些值从转换String为属性或参数的理论类型。以下示例显示了设置的各种值:

<bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">    <!-- results in a setDriverClassName(String) call -->    <property name="driverClassName" value="com.mysql.jdbc.Driver"/>    <property name="url" value="jdbc:mysql://localhost:3306/mydb"/>    <property name="username" value="root"/>    <property name="password" value="misterkaoli"/></bean><beans xmlns="http://www.springframework.org/schema/beans"    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    xmlns:p="http://www.springframework.org/schema/p"    xsi:schemaLocation="http://www.springframework.org/schema/beans    https://www.springframework.org/schema/beans/spring-beans.xsd">    <bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource"        destroy-method="close"        p:driverClassName="com.mysql.jdbc.Driver"        p:url="jdbc:mysql://localhost:3306/mydb"        p:username="root"        p:password="misterkaoli"/></beans>

后面的XML更简洁。然而,拼写错误是在运行时而不是设计时发现的,除非您应用IDE(例如IntelliJ IDEA或用于Eclipse的Spring工具)在创立bean定义时反对主动属性实现。

您还能够配置java.util.Properties实例,如下所示:

<bean id="mappings"    class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">    <property name="properties">        <value>            jdbc.driver.className=com.mysql.jdbc.Driver            jdbc.url=jdbc:mysql://localhost:3306/mydb        </value>    </property></bean>

Spring容器将<value></value>元素中的文本转换为java.util。属性实例,通过应用JavaBeans的PropertyEditor机制。这是一个很好的快捷方式,也是Spring团队偏爱应用嵌套的<value></value>元素而不是value属性款式的几个中央之一。

idref元素

所述idref元件是一个防错办法,次要通过将该容器中的另一个bean的id(将id作为一个字符串值传递-而不是援用)传递给<constructor-arg/>或<property/>标签。

以下示例显示了如何应用它:

<bean id="theTargetBean" class="..."/><bean id="theClientBean" class="...">    <property name="targetName">        //此处将 theTargetBean 作为字符串传递给 theClientBean的targetName属性        //而不是将theTargetName这个bean的实例传递给targetName属性        <idref bean="theTargetBean"/>    </property></bean>

后面的bean定义代码段(在运行时)与以下代码段齐全等效:
idref 等价的是 value标签 而不是 ref标签

<bean id="theTargetBean" class="..." /><bean id="client" class="...">    <property name="targetName" value="theTargetBean"/></bean>
元素 的local属性在idref4.0 Bean XSD中不再受反对,因为它不再提供惯例bean援用上的值。降级到4.0模式时,将现有idref local援用更改为idref bean。

idref用法 能够校验传入的作为bean的id 会被用来校验以后id的bean存不存在

<idref></idref>元素罕用的地位(至多在spring2.0之前的版本中)是在ProxyFactoryBean bean定义中的AOP拦截器配置中。在指定拦截器名称时应用<idref></idref>元素能够避免拼写错误。
ref 对其余Bean的援用

ref元素是<constructor-arg>或</property>定义元素中的最初一个元素。在这里,您将bean的指定属性的值设置为对容器治理的另一个bean的援用。被援用的bean是要设置其属性的bean的依赖项,并且在设置属性之前依据须要初始化它(如果协作者是单例bean,它可能曾经被容器初始化了)。所有援用最终都是对另一个对象的援用。范畴和验证取决于是否通过bean或父属性指定其余对象的ID或名称。

通过<ref></ref>标记的bean属性指定指标bean是最通用的模式,它容许创立对同一容器或父容器中任何bean的援用,而不论它是否在同一XML文件中。bean属性的值能够与指标bean的id属性雷同,或者与指标bean的name属性中的一个值雷同。上面的例子展现了如何应用ref元素:

//someBean 能够是bean的id  也能够是bean的name<ref bean="someBean"/>

ref元素的local属性在ref4.0 Bean XSD中不再受反对,
因为它不再提供惯例bean援用上的值。降级到4.0模式时,将现有ref local援用更改ref bean为。

汇合

<list></list>、<set></set>、<map></map>、</props>元素别离设置Java汇合类型list、set、map、properties的属性和参数。上面的例子展现了如何应用它们:

<bean id="moreComplexObject" class="example.ComplexObject">    <!-- results in a setAdminEmails(java.util.Properties) call -->    <property name="adminEmails">        <props>            <prop key="administrator">administrator@example.org</prop>            <prop key="support">support@example.org</prop>            <prop key="development">development@example.org</prop>        </props>    </property>    <!-- results in a setSomeList(java.util.List) call -->    <property name="someList">        <list>            <value>a list element followed by a reference</value>            <ref bean="myDataSource" />        </list>    </property>    <!-- results in a setSomeMap(java.util.Map) call -->    <property name="someMap">        <map>            <entry key="an entry" value="just some string"/>            <entry key ="a ref" value-ref="myDataSource"/>        </map>    </property>    <!-- results in a setSomeSet(java.util.Set) call -->    <property name="someSet">        <set>            <value>just some string</value>            <ref bean="myDataSource" />        </set>    </property></bean>

map的key或value,或set的value 也能够是以下任意元素:

bean | ref | idref | list | set | map | props | value | null
汇合合并

Spring容器还反对合并汇合。利用开发者能够定义一个父元素<list></list>、<map></map>、<set></set>或</props>,让子元素<list></list>、<map></map>、<set></set>或</props>继承和笼罩父元素汇合的值。也就是说,子集合的值是合并父汇合和子集合的元素的后果,子集合元素笼罩父汇合中指定的值。

对于合并的这一节探讨父-子bean机制。不相熟父bean和子bean定义的读者可能心愿在持续之前浏览相干局部。

上面的例子演示了汇合合并:

<beans>    <bean id="parent" abstract="true" class="example.ComplexObject">        <property name="adminEmails">            <props>                <prop key="administrator">administrator@example.com</prop>                <prop key="support">support@example.com</prop>            </props>        </property>    </bean>    <bean id="child" parent="parent">        <property name="adminEmails">            <!-- the merge is specified on the child collection definition -->            <props merge="true">                <prop key="sales">sales@example.com</prop>                <prop key="support">support@example.co.uk</prop>            </props>        </property>    </bean><beans>

留神在子bean定义的adminEmails属性的</props>元素上应用了merge=true属性。当容器解析并实例化子bean时,生成的实例具备一个adminEmails属性汇合,该汇合蕴含将子bean的adminEmails汇合与父bean的adminEmails汇合合并的后果。上面的清单显示了后果:

administrator=administrator@example.comsales=sales@example.comsupport=support@example.co.uk

子属性汇合的值集继承父属性</props>中的所有属性元素,子属性反对值的值笼罩父属性汇合中的值。

这种合并行为相似地利用于<list></list>、<map></map>和<set></set>汇合类型。在<list></list>元素的特定状况下,将保护与列表汇合类型(即值的有序汇合的概念)相关联的语义。父元素的值位于所有子元素列表的值之前。对于Map、Set和Properties汇合类型,不存在排序。因而,对于位于容器外部应用的关联映射、汇合和属性实现类型下的汇合类型,排序语义不起作用。

汇合合并的局限性

您不能合并不同的汇合类型(例如Map和List)。如果尝试这样做,将会抛出异样。
该merge属性必须在上面的继承的子集合定义中指定。
merge在父汇合定义上指定属性是多余的,不会导致所需的合并。

强类型汇合

随着Java 5中泛型类型的引入,您能够应用强类型汇合。也就是说,能够申明一个Collection类型,使其仅蕴含(例如)String元素。如果应用Spring将强类型依赖注入Collection到Bean中,则能够利用Spring的类型转换反对,以便在将强类型Collection 实例的元素增加到之前将其转换为适当的类型Collection。以下Java类和bean定义显示了如何执行此操作:

public class SomeClass {    private Map<String, Float> accounts;    public void setAccounts(Map<String, Float> accounts) {        this.accounts = accounts;    }}<beans>    <bean id="something" class="x.y.SomeClass">        <property name="accounts">            <map>                <entry key="one" value="9.99"/>                <entry key="two" value="2.75"/>                <entry key="six" value="3.99"/>            </map>        </property>    </bean></beans>当筹备注入bean 的accounts属性时,能够通过反射取得something无关强类型的元素类型的泛型信息Map<String,Float>。因而,Spring的类型转换根底构造将各种值元素辨认为type Float,并将字符串值(9.99, 2.75和 3.99)转换为理论Float类型。
空字符串值和空字符串值

Spring将属性之类的空参数视为空字符串。以下基于xml的配置元数据片段将email属性设置为空字符串值("")。

<bean class="ExampleBean">    <property name="email" value=""/></bean>

后面的示例等效于以下Java代码:

exampleBean.setEmail("");

该<null/>元素解决null的值。以下清单显示了一个示例:

<bean class="ExampleBean">    <property name="email">        <null/>    </property></bean>

后面的配置等效于上面的Java代码:

exampleBean.setEmail(null);
带有p-名称空间的XML快捷方式

p-名称空间容许您应用bean元素的属性(而不是嵌套的</property>元素)来形容与之单干的bean的属性值,或者两者都应用。

Spring反对带有名称空间的可扩大配置格局,名称空间基于XML模式定义。本章中探讨的bean配置格局是在XML模式文档中定义的。然而,p-名称空间没有在XSD文件中定义,只存在于Spring的外围中。

上面的示例显示了两个解析为雷同后果的XML片段(第一个应用规范XML格局,第二个应用p-名称空间):

<beans xmlns="http://www.springframework.org/schema/beans"    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    xmlns:p="http://www.springframework.org/schema/p" <!-- 这一行是要的 -->    xsi:schemaLocation="http://www.springframework.org/schema/beans        https://www.springframework.org/schema/beans/spring-beans.xsd">    <bean name="classic" class="com.example.ExampleBean">        <property name="email" value="someone@somewhere.com"/>    </bean>    <bean name="p-namespace" class="com.example.ExampleBean"        p:email="someone@somewhere.com"/></beans>

该示例显示了email在bean定义中调用的p-namespace中的属性。这通知Spring蕴含一个属性申明。如前所述,p名称空间没有架构定义,因而能够将属性名称设置为属性名称。

下一个示例包含另外两个bean定义,它们都援用了另一个bean:

<beans xmlns="http://www.springframework.org/schema/beans"    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    xmlns:p="http://www.springframework.org/schema/p"     xsi:schemaLocation="http://www.springframework.org/schema/beans        https://www.springframework.org/schema/beans/spring-beans.xsd">    <bean name="john-classic" class="com.example.Person">        <property name="name" value="John Doe"/>        <property name="spouse" ref="jane"/>    </bean>    <bean name="john-modern"        class="com.example.Person"        p:name="John Doe"        p:spouse-ref="jane"/>    <bean name="jane" class="com.example.Person">        <property name="name" value="Jane Doe"/>    </bean></beans>

这个示例不仅蕴含一个应用p-名称空间的属性值,而且还应用一种非凡格局来申明属性援用。第一个bean定义应用</property>来创立一个从bean john到bean jane的援用,第二个bean定义应用p:spouse-ref="jane"作为一个属性来实现完全相同的工作。在本例中,spouse是属性名,而-ref局部表明这不是一个间接的值,而是对另一个bean的援用。

    p-名称空间不如规范XML格局灵便。    例如,申明属性援用的格局与以Ref结尾的属性抵触,而规范XML格局不会。    咱们建议您认真抉择您的办法,并与您的团队成员沟通,    以防止同时生成应用所有三种办法的XML文档。    The p-namespace is not as flexible as the standard XML format.     For example, the format for declaring property references clashes     with properties that end in Ref,whereas the standard XML format does not.    We recommend that you choose your approach carefully     and communicate this to your team members to avoid producing XML documents    that use all three approaches at the same time.        这段没看懂,这里测试了以Ref结尾的属性也是能够用 p:xxxRef-ref
具备c-namespace的XML快捷方式

与应用p-namespace的XML快捷方式相似,Spring 3.1中引入的c-namespace容许应用内联属性配置结构函数参数,而不是嵌套结构函数参数元素。说白了就是p-namespace替换property,c-namespace替换constructor-arg

上面的示例应用c:名称空间执行与 基于构造函数的依赖注入雷同的操作:

<beans xmlns="http://www.springframework.org/schema/beans"    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    xmlns:c="http://www.springframework.org/schema/c" <!-- 新增该条 -->    xsi:schemaLocation="http://www.springframework.org/schema/beans        https://www.springframework.org/schema/beans/spring-beans.xsd">    <bean id="beanTwo" class="x.y.ThingTwo"/>    <bean id="beanThree" class="x.y.ThingThree"/>    <!-- 带有可选参数名称的传统申明 -->    <bean id="beanOne" class="x.y.ThingOne">        <constructor-arg name="thingTwo" ref="beanTwo"/>        <constructor-arg name="thingThree" ref="beanThree"/>        <constructor-arg name="email" value="something@somewhere.com"/>    </bean>    <!-- 带有参数名称的c-名称空间申明 -->    <bean id="beanOne" class="x.y.ThingOne" c:thingTwo-ref="beanTwo"        c:thingThree-ref="beanThree" c:email="something@somewhere.com"/></beans>

对于结构函数参数名不可用的常见状况(通常是在编译字节码时没有调试信息的状况下),能够应用回退到参数索引,如下所示:

<bean id="beanOne" class="x.y.ThingOne" c:_0-ref="beanTwo" c:_1-ref="beanThree"    c:_2="something@somewhere.com"/>
复合属性名称 一个bean中嵌套另外一个bean 并须要给外部的bean赋值

在设置bean属性时,能够应用复合或嵌套属性名,只有门路的所有组件(最终属性名除外)都不为空。思考上面的bean定义:

<bean id="something" class="things.ThingOne">    <property name="fred.bob.sammy" value="123" /></bean>所述something bean具备fred属性,该属性具备bob属性,bob属性具备sammy 个性,并且最终sammy属性被设置为值123。  something bean的fred属性和fred的bob属性在结构bean之后肯定不能为null。  否则,将会引发NullPointerException。
1.4.3。应用depends-on

如果一个bean是另一个bean的依赖项,则通常意味着将一个bean设置为另一个bean的属性。通常,您能够应用基于XML的配置元数据中的<ref/> 元素来实现此操作。
然而,有时bean之间的依赖性不太间接。一个示例是何时须要触发类中的动态初始值设定项,例如用于数据库驱动程序注册。该depends-on属性能够在初始化应用此元素的bean之前显式强制初始化一个或多个bean。以下示例应用该depends-on属性示意对单个bean的依赖关系:

<bean id="beanOne" class="ExampleBean" depends-on="manager"/><bean id="manager" class="ManagerBean" />

要表白对多个bean的依赖性,就须要用逗号隔开多个名称

<bean id="beanOne" class="ExampleBean" depends-on="manager,accountDao">    <property name="manager" ref="manager" /></bean><bean id="manager" class="ManagerBean" /><bean id="accountDao" class="x.y.jdbc.JdbcAccountDao" />
depends-on属既能够指定初始化工夫依赖项,也能够指定对应的销毁工夫依赖项(仅在单例bean中)。被依赖的bean会晚于依赖bean之后销毁<bean id="serviceOneRef" name="serviceOneName" class="org.springframework.example.service.ServiceOne"          destroy-method="destroyed"/>    public void destroyed(){        System.out.println("ServiceOne destroy");    }    <bean id="serviceTwo" class="org.springframework.example.service.ServiceTwo"          p:name="asdfasdf"  depends-on="serviceOneRef"          destroy-method="destroyed" />    public void destroyed(){        System.out.println("serviceTwo destroy");    }          console:    serviceTwo destroy    ServiceOne destroy
1.4.4。懒加载bean

默认状况下,作为初始化过程的一部分,ApplicationContext实现会急迫地创立和配置所有的单例bean。通常,这种预实例化是可取的,因为配置或周围环境中的谬误是立刻发现的,而不是几小时甚至几天后发现的。
当这种行为不适合时,您能够通过将bean定义标记为提早初始化来避免单例bean的预实例化。提早初始化的bean通知IoC容器在第一次申请bean实例时(而不是在启动时)创立bean实例。

在XML中,这种行为是由<bean></bean>元素的lazy-init属性管制的,如上面的例子所示:

<bean id="lazy" class="com.something.ExpensiveToCreateBean" lazy-init="true"/><bean name="not.lazy" class="com.something.AnotherBean"/>

当ApplicationContext应用后面的配置时,
lazy bean不会在ApplicationContext启动时急迫地预实例化,
not.lazy bean则会被急迫地预实例化。

然而,当懒加载的bean是非懒加载的单例bean的依赖项时,ApplicationContext在启动时则会创立懒加载的bean,因为它必须满足单例的依赖项。

您还能够通过应用<beans></beans>元素的default-lazy-init批量设置懒加载bean

<beans default-lazy-init="true">    <!-- no beans will be pre-instantiated... --></beans>
1.4.5。主动拆卸

Spring容器能够主动拆卸合作bean之间的关系。
主动拆卸具备以下长处:

  • 主动拆卸能够大大减少指定属性或结构函数参数的须要。
  • 随着对象的倒退,主动拆卸能够更新配置。例如,如果您须要向类中增加一个依赖项,则无需批改配置即可主动满足该依赖项。因而,主动拆卸在开发过程中特地有用,而不用放心当代码库变得更稳固时切换到显式接线的抉择。

应用基于XML的配置元数据时,能够应用元素的autowire属性为 bean定义指定主动拆卸模式<bean/>。主动拆卸性能具备四种模式。您能够为每个bean指定主动拆卸,因而能够抉择要主动拆卸的拆卸。下表形容了四种主动拆卸模式:

模式解释
no(默认)没有主动拆卸。想要援用其余的Bean必须由ref元素定义。
对于较大的部署,不倡议更改默认设置,
因为显式地指定须要援用的bean 能够提供更好的管制和清晰度。
在某种程度上,它记录了零碎的构造。
byName按属性名称主动拆卸。
Spring寻找与须要主动实现的属性同名的bean。
例如,如果一个bean定义autowire模式设置为byName,
并且它蕴含一个master属性(也就是说,它有一个setMaster(..)办法)
,那么Spring将查找一个名为master的bean定义,
并应用它来设置该属性。
次要还是依据set办法来确定属性名,如果你有master属性,
然而你的set办法是setMyMaster(..),
那么Spring会查找名为 myMaster的bean而不是名为 master的bean
byType实用于set 办法的入参类型,
如果容器中恰好存在该属性类型的一个bean,则容许该属性主动注入。
如果存在多个,就会抛出一个致命异样,
这表明您不能对该bean应用byType主动拆卸。
如果没有匹配的bean,则什么也不会产生(没有设置属性)。
constructor相似于byType,但实用于结构函数参数。
如果容器中没有结构函数参数类型的确切bean,就会引发致命谬误。

应用byType或constructor主动拆卸模式,您能够主动注入arrays和collections类型。在这种状况下,将提供容器中与冀望类型匹配的所有主动拆卸候选,以满足相关性。
如果接管的map 的key值类型为String,那么你也能够让Spring主动拆卸Map类型的值,并且 Map实例的key值为相应的bean名称。

    @Autowired    private List<CusService> serviceLists;    @Autowired    private Map<String,CusService> cusServiceMap;
主动接线的局限性和毛病

主动拆卸在我的项目中统一应用时工作得最好。如果主动拆卸没有被广泛应用,那么应用它来连贯一个或两个bean定义可能会使局部开发人员感到困惑。

思考主动拆卸的局限性和毛病:

  • 属性和结构参数设置中的显式依赖关系总是笼罩主动拆卸。您不能主动连贯简略属性,如primitives(boolean,int,long等)、String和classes(以及此类简略属性的数组)。这种限度被刻意设计的。
  • 主动拆卸bean不如显式指定bean准确。不过,正如后面的表中所指出的,Spring曾经尽可能防止产生意外后果。Spring治理的对象之间的关系不再被明确地记录。
  • 对于能从Spring容器生成文档的工具来说生成连贯信息是不可能的了。
  • 主动注入依赖项时如果有多个能够匹配的选项,如果注入类型是数组、汇合或映射实例,这不是问题。然而,对于冀望应用单个值的依赖项,这种模糊性不能任意解决。如果没有可用的惟一bean定义,则抛出异样。

在后一种状况下,您有几个选项:

  • 放弃主动拆卸,反对显式布线。
  • 通过将bean定义的autowire-candidate设置为false来防止主动拆卸。
  • 通过将单个bean定义的<bean></bean>元素的primary设置为true,将其指定为主候选bean定义。
  • 应用基于正文的配置实现更细粒度的管制,1.9大节会专门解说注解应用。
1.放弃主动拆卸,改成指定bean注入 @Qualifier 或者 xml的ref属性都能够2.        <bean id="serviceOne"  class="org.springframework.example.service.ServiceOne" />        <bean id="serviceTwo"  class="org.springframework.example.service.ServiceTwo" />    两个bean都继承同一个接口CusService,如果有主动拆卸如下        @Autowired        private CusService service;    则启动时候会报错    如果给serviceOne减少属性autowire-candidate="false"         <bean id="serviceOne"  class="org.springframework.example.service.ServiceOne" autowire-candidate="false" />    则所有的主动拆卸CusService的接口都会优先拆卸serviceTwo3.状况同2 还能够将serviceTwo 减少primary="true"         <bean id="serviceTwo"  class="org.springframework.example.service.ServiceTwo" primary="true" />
从主动拆卸中排除Bean

在每个bean的根底上,您能够从主动拆卸中排除一个bean。在Spring的XML格局中,将<bean></bean>元素的autowire-candidate设置为false。容器使得特定的bean定义对主动拆卸基础设施不可用(包含正文格调配置,如@Autowired)。

autowire-candidate属性被设计为只影响基于类型的主动拆卸。它不影响按名称的显式援用,即便指定的bean没有标记为主动拆卸候选,也会解析显式援用。因而,如果名称匹配,按名称主动拆卸依然会注入一个bean。

<beans></beans>元素在其default-autowire-candidates属性接管一个patterns 字符串,意思是依据patterns 字符串匹配到的所有合格的beanName的autowire-candidates会被设置为true,不合格的会被设置为false
patterns 字符串承受一个或多个匹配模式,多个patterns 字符串之间能够用逗号隔开。 例如 (Repository,Service,*Dao) 这种组合模式

bean自身的autowire-candidates属性优于<beans>的default-autowire-candidates属性失效

这些技术对于那些您永远不心愿通过主动拆卸被注入到其余bean中的bean十分有用。这并不意味着被排除的bean自身不能应用主动拆卸进行配置。相同,该bean自身仅仅是不作为其余bean的主动连贯候选对象。

        public static final String AUTOWIRE_CANDIDATE_ATTRIBUTE = "autowire-candidate";        //获取autowire-candidate  这个值默认就是true        String autowireCandidate = ele.getAttribute(AUTOWIRE_CANDIDATE_ATTRIBUTE);        //判断 是不是定义的以后bean是不是 default        if (isDefaultValue(autowireCandidate)) {            //如果是default             //查找以后bean 所在beans的default-autowire-candidates属性 找到配置的patterns表达式            //如果表达式为空 就不解决setAutowireCandidate属性值 这样该属性仍旧是true            String candidatePattern = this.defaults.getAutowireCandidates();            if (candidatePattern != null) {                //表达式不为空 判断以后beanName 是否在表达式范畴内                //在范畴内就setAutowireCandidate设置为true                //否则设置为false                String[] patterns = StringUtils.commaDelimitedListToStringArray(candidatePattern);                bd.setAutowireCandidate(PatternMatchUtils.simpleMatch(patterns, beanName));            }        }        else {        //如果autowireCandidate 不是 default 是true 那就设置为true         //是false 那就设置为false            bd.setAutowireCandidate(TRUE_VALUE.equals(autowireCandidate));        }
1.4.6。办法注入

在大多数利用场景中,容器中的大多数bean是 singletons。
当单例Bean须要与另一个单例Bean合作或非单例Bean须要与另一个非单例Bean合作时,通常能够通过将一个Bean定义为另一个Bean的属性来解决依赖性,生命周期雷同的类相互注入时没有问题。
当bean的生命周期不同时会呈现问题。假如单例bean A须要应用非单例(原型)bean B,兴许在A的每个办法调用上都应用。容器仅创立一次单例bean A,因而只有一次机会来设置属性。每次须要一个容器时,容器都无奈为bean A提供一个新的bean B实例。

一个解决方案是放弃某些管制反转。您能够通过实现接口ApplicationContextAware ,并使每次容器 A都须要容器 B 的调用来申请(通常是新的)bean B实例,从而使bean A晓得容器。以下示例显示了此办法:ApplicationContextAware.getBean("B")

// a class that uses a stateful Command-style class to perform some processingpackage fiona.apple;// Spring-API importsimport org.springframework.beans.BeansException;import org.springframework.context.ApplicationContext;import org.springframework.context.ApplicationContextAware;public class CommandManager implements ApplicationContextAware {    private ApplicationContext applicationContext;    public Object process(Map commandState) {        // grab a new instance of the appropriate Command        Command command = createCommand();        // set the state on the (hopefully brand new) Command instance        command.setState(commandState);        return command.execute();    }    protected Command createCommand() {        // notice the Spring API dependency!        return this.applicationContext.getBean("command", Command.class);    }    public void setApplicationContext(            ApplicationContext applicationContext) throws BeansException {        this.applicationContext = applicationContext;    }}

后面的内容是不现实的,因为业务代码晓得并耦合到Spring框架。办法注入是Spring IoC容器的一项高级性能,使您能够洁净地解决此用例。

您能够在此博客条目中理解无关办法注入动机的更多信息。

Lookup Method注入

Lookup Method注入是指容器笼罩容器治理bean上的办法并返回容器中另一个已命名bean的查找后果的能力。查找通常波及原型bean,如上一节所述的场景。Spring框架通过应用来自CGLIB库的字节码生成动静生成笼罩该办法的子类来实现这种办法注入。

  • 要使这个动静子类工作,Spring bean容器子类的类不能是final,要笼罩的办法也不能是final。
  • 单元测试具备形象办法的类须要您本人创立类的子类,并提供形象办法的存根实现。
  • 具体的办法对于组件扫描也是必要的,这须要具体的类来拾取。
  • 另一个要害的限度是,Lookup Method 不能与工厂办法一起工作,特地是与配置类中的@Bean办法一起工作,因为在这种状况下,容器不负责创立实例,因而不能动静地创立运行时生成的子类。

对于CommandManager后面的代码片段中的类,Spring容器动静地笼罩该createCommand() 办法的实现。该CommandManager班没有任何Spring的依赖,如下所示:

package fiona.apple;public abstract class CommandManager {    public Object process(Object commandState) {        // 获取适当的命令接口的新实例        Command command = createCommand();        // 在(心愿是全新的)命令实例上设置状态        command.setState(commandState);        return command.execute();    }    // 实现类在哪呢???    protected abstract Command createCommand();}

在蕴含要注入的办法的客户端类中(本例为CommandManager),要注入的办法须要以下模式的签名:

<public|protected> [abstract] <return-type> theMethodName(no-arguments);

如果办法为abstract,则动静生成的子类将实现该办法。否则,动静生成的子类将笼罩原始类中定义的具体方法。思考以下示例:

<!-- 作为原型部署的有状态bean(非单例)--><bean id="myCommand" class="fiona.apple.AsyncCommand" scope="prototype">    <!-- inject dependencies here as required --></bean><!-- commandProcessor应用statefulCommandHelper--><bean id="commandManager" class="fiona.apple.CommandManager">    <lookup-method name="createCommand" bean="myCommand"/></bean>

标识为commandManager的bean在须要myCommand bean的新实例时调用它本人的createCommand()办法。是否要将myCommand bean部署为原型,必须认真确认本人的需要。如果是单例,则每次都返回雷同的myCommand bean实例。

或者,在基于正文的组件模型中,您能够通过@Lookup正文申明一个查找办法,如上面的示例所示:

public abstract class CommandManager {    public Object process(Object commandState) {        Command command = createCommand();        command.setState(commandState);        return command.execute();    }    @Lookup("myCommand")    protected abstract Command createCommand();}

或者,更习用的是,您能够依赖于指标bean依据查找办法的申明的返回类型来解析:

public abstract class CommandManager {    public Object process(Object commandState) {        MyCommand command = createCommand();        command.setState(commandState);        return command.execute();    }    @Lookup    protected abstract MyCommand createCommand();}

请留神,您通常应该应用具体的存根实现来申明这种带正文的查找办法,以便它们与Spring的组件扫描规定兼容,其中抽象类在默认状况下会被疏忽。此限度不适用于显式注册或显式导入的bean类。

拜访范畴不同的指标bean的另一种办法是ObjectFactory/ Provider注入点(https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/core.html#beans-factory-scopes-other-injection)。您可能还会发现ServiceLocatorFactoryBean(在 org.springframework.beans.factory.config包装中)有用。
任意办法替换

办法替换注入的模式是用另一个办法实现替换托管bean中的任意办法的能力。这个非常不罕用,您能够跳过本节的其余部分,等到您真正须要此性能再来看。

对于基于xml的配置元数据,您能够应用replaced-method元素将一个已部署bean的现有办法实现替换为另一个办法实现。思考上面的类,它有一个名为computeValue的办法,咱们想要笼罩它:

public class MyValueCalculator {    public String computeValue(String input) {        // some real code...    }}

实现该org.springframework.beans.factory.support.MethodReplacer 接口的类提供了新的办法定义,如以下示例所示:

/** *用于笼罩现有的computeValue(String input) *实现在MyValueCalculator */public class ReplacementComputeValue implements MethodReplacer {    public Object reimplement(Object o, Method m, Object[] args) throws Throwable {        //获取输出值,应用它,并返回计算结果        String input = (String) args[0];        ...        return ...;    }}

用于部署原始类并指定办法笼罩的Bean定义相似于以下示例:

<bean id="myValueCalculator" class="x.y.z.MyValueCalculator">    <replaced-method name="computeValue" replacer="replacementComputeValue">        <arg-type>String</arg-type>    </replaced-method></bean><bean id="replacementComputeValue" class="a.b.c.ReplacementComputeValue"/>

您能够在<replaced-method/>元素内应用一个或多个<arg-type/>元素 来批示要笼罩的办法的办法签名。仅当办法重载且类中存在多个变体时,才须要对参数签名。为了不便起见,参数的类型字符串能够是齐全限定类型名称的子字符串。
例如,以下所有都是java.lang.String匹配项 :

java.lang.StringStringStr

因为参数的数量通常足以辨别每个可能的抉择,所以通过让您仅键入与参数类型匹配的最短字符串,此快捷方式能够节俭很多输出