SpringFramework核心技术一(IOC:详细的依赖和配置)

本文涉及的产品
云数据库 RDS MySQL Serverless,0.5-2RCU 50GB
简介: 详细的依赖和配置如前一节所述,您可以将bean属性和构造函数参数定义为对其他受管Bean(协作者)的引用,或者将其定义为内联定义的值。

详细的依赖和配置

如前一节所述,您可以将bean属性和构造函数参数定义为对其他受管Bean(协作者)的引用,或者将其定义为内联定义的值。Spring的基于XML的配置元数据为此支持其元素<property/><constructor-arg/>元素中的子元素类型 。


一、几种依赖配置的类型

  • 1.直接值(基元,字符串等)
    在value所述的属性元素指定属性或构造器参数的人类可读的字符串表示。Spring的 转换服务用于将这些值从a转换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="masterkaoli"/>
</bean>
  • 2.以下示例使用p-namespace进行更简洁的XML配置。
<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
    http://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="masterkaoli"/>

</beans>
  • 3.前面的XML更简洁; 然而,在运行时而不是设计时发现错字,除非您在创建bean定义时使用支持自动属性完成的IDE(如IntelliJ IDEA或Spring Tool Suite(STS))。强烈建议这种IDE帮助。
  • 4.基于java.util.Properties的配置
    Spring容器通过使用JavaBeans 机制将元素内部的文本转换为 java.util.Properties实例PropertyEditor。这是一个很好的捷径,它是Spring团队倾向于<value/>在值属性样式上使用嵌套元素的少数。
<bean id="mappings"
    class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">

    <!-- typed as a java.util.Properties -->
    <property name="properties">
        <value>
            jdbc.driver.className=com.mysql.jdbc.Driver
            jdbc.url=jdbc:mysql://localhost:3306/mydb
        </value>
    </property>
</bean>
  • 5 idref元素该idref元素只是一种防错的方式,将容器中另一个bean的id(字符串值 - 不是引用)传递给 < constructor-arg />或 <属性/>元素。
<bean id="theTargetBean" class="..."/>

<bean id="theClientBean" class="...">
    <property name="targetName">
        <idref bean="theTargetBean"/>
    </property>
</bean>
  • 6 上面的bean定义片段与下面的片段完全等价(在运行时):
<bean id="theTargetBean" class="..." />

<bean id="client" class="...">
    <property name="targetName" value="theTargetBean"/>
</bean>
  • 7 5中的idref比6中的形式更可取,因为使用idref标签允许容器在部署时验证所引用的命名bean实际存在。在第二种变体中,不会对传递给bean targetName属性的值执行验证client。当clientbean实际实例化时,只会发现错误(最有可能致命的结果)。如果这个client bean是一个原型 bean,那么这个错字和产生的异常可能只会在容器被部署后很长时间才被发现。

  • 8 4.0 beans xsd不再支持本地该idref元素的属性,因为它不再提供超过常规bean引用的值。只需将现有idref本地引用更改为idref bean升级到4.0模式时。

  • 9 其中一个共同的地方(至少在早期比Spring 2.0版本)< idref />元素带来的值在配置AOP拦截在ProxyFactoryBeanbean定义。< idref />当您指定拦截器名称时使用元素可以防止拼写错误拦截器ID。

二、引用其他Bean类

1 ref

  • ref -该元素是一个 <constructor-arg /> <property />
    定义元素中的最后一个元素。在这里,您将bean的指定属性的值设置为对由容器管理的另一个bean(协作者)的引用。被引用的bean是其属性将被设置的bean的依赖项,并且在属性设置之前根据需要初始化它。(如果协作者是单例bean,它可能已被容器初始化。)所有引用最终都是对另一个对象的引用。划定范围和有效性取决于是否通过指定其他对象的ID /名称bean,local,或parent属性。
  • 通过标记的bean属性指定目标bean 是最通用的形式,并且允许创建对同一个容器或父容器中的任何bean的引用,而不管它是否位于同一个XML文件中。该bean属性的值可能id与目标bean 的属性相同,或者与目标bean属性中的值之一 相同name。
<ref bean="someBean"/>

2 对当前容器的父容器中的bean的引用

  • 通过parent属性指定目标Bean 将创建对当前容器的父容器中的bean的引用。该parent 属性的值可能id与目标bean 的属性或目标bean 的属性中的一个值相同name,并且目标bean必须位于当前bean的父容器中。您主要在具有容器层次结构时使用此bean参考变体,并且想要使用与父bean名称相同的代理将父容器中的现有bean包装在父容器中。
  • 父容器中的bean
<!-- in the parent context -->
<bean id="accountService" class="com.foo.SimpleAccountService">
    <!-- insert dependencies as required as here -->
</bean>
  • 子容器中的bean
<!-- in the child (descendant) context -->
<bean id="accountService" <!-- bean name is the same as the parent bean -->
    class="org.springframework.aop.framework.ProxyFactoryBean">
    <property name="target">
        <ref parent="accountService"/> <!-- notice how we refer to the parent bean -->
    </property>
    <!-- insert other configuration and dependencies as required here -->
</bean>

4.0 beans xsd不再支持local该ref元素的属性,因为它不再提供超过常规bean引用的值。只需将现有ref local引用更改为ref bean升级到4.0模式时。

三、内部类

<bean/>内部的元件<property/><constructor-arg/>元件定义了一个所谓的内部bean。

<bean id="outer" class="...">
    <!-- instead of using a reference to a target bean, simply define the target bean inline -->
    <property name="target">
        <bean class="com.example.Person"> <!-- this is the inner bean -->
            <property name="name" value="Fiona Apple"/>
            <property name="age" value="25"/>
        </bean>
    </property>
</bean>
  • 定义一个内部类A,不要求定义A的id和名字。如果你偏要给内部类定义Id和名字,容器并不会在内部类中使用这些属性。容器在创建内部类的时候,也会忽略你指定的Id和名字。
  • 内部类始终是匿名的,并且内部类是在创建外部类的时候才能创建的。是不可能将内部类直接注入到其他Bean中,也是不能够脱离外部Bean直接去访问内部类的。
  • 作为一个实例,它是可以从自定义范围内去接收销毁回调函数。
    例如:对于包含在单例bean中的请求范围的内部Bean:创建内部Bean实例将绑定到它的包含bean上,但是销毁回调函数将允许它参数到请求的生命周期中去。
    这不是一个常见的情况,内部类通常只是简单的分享它的bean的范围。

四、集合

1 集合说明

<list/><set/><map/>,和<props/>元素,你将Java的性能和Collection类型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>

映射键或值或设置值的值也可以是以下任何元素:

bean | ref | idref | list | set | map | props | value | null

2 集合合并

  • 1.Spring容器也支持集合的合并。应用程序开发人员可以定义一个父风格<list/><map/><set/><props/>元素,并有孩子式的<list/><map/><set/><props/>元素继承和父集合覆盖值。也就是说,最终集合的值是合并父集合和子集合元素的结果。
<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>
  • 2.注意在bean定义的merge=true属性的<props/>元素上使用adminEmails属性的孩子。当childbean被容器解析并实例化时,生成的实例具有一个adminEmails属性集合,该集合包含合并子集合adminEmails与父adminEmails集合的结果。
administrator=administrator@example.com 
sales=sales@example.com 
support = support@example.co.uk
  • 孩子Properties集合的值设置继承父所有属性元素<props/>,和孩子的为值support值将覆盖父集合的价值。
    这一合并行为同样适用于<list/><map/><set/>集合类型。在<list/>元素的特定情况下,与List集合类型相关联的语义(即ordered 值集合的概念)被保留; 父项的值在所有子项列表的值之前。在的情况下Map,Set和Properties集合类型,没有顺序存在。因此,没有排序的语义在背后的关联的集合类型的效果Map,Set以及Properties该容器内部使用实现类型。

  • 3.集合合并的限制
    您不能合并不同的集合类型(如a Map和a List),并且如果您确实尝试这样做,Exception则会引发适当的集合类型。该merge属性必须在较低的继承的子定义上指定; merge在父集合定义上指定属性是多余的,并且不会导致所需的合并。

  • 4.合并强类型集合
    随着Java 5中引入泛型类型,您可以使用强类型集合。也就是说,可以声明一个Collection只能包含 String元素的类型(例如)。如果您使用Spring将强类型依赖注入Collection到bean中,则可以利用Spring的类型转换支持,以便强类型Collection 实例的元素在添加到类中之前转换为适当的类型Collection。

public class Foo {

    private Map<String, Float> accounts;

    public void setAccounts(Map<String, Float> accounts) {
        this.accounts = accounts;
    }
}

.xml文件配置

<beans>
    <bean id="foo" class="x.y.Foo">
        <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>

当豆的帐户属性foo的准备注入时,有关强类型元素类型的泛型信息 Map <String,Float> 可通过反射获得。因此,Spring的类型转换基础结构将各种值元素识别为类型Float和字符串值9.99,2.75,将3。99其转换为实际浮点数类型。

五,空值和字符串

5.空值春季将属性的空值视为空白的字符串。

  • 以下是基于xml文件配置元数据将emai属性设置为空String值(”“)
<bean class="ExampleBean">
    <property name="email" value=""/>
</bean>
  • 等于下面的java代码
exampleBean.setEmail("");
  • <null/>元素来处理null值
<bean class="ExampleBean">
    <property name="email">
        <null/>
    </property>
</bean>

六、带有p命名空间的XML快捷方式(不推荐使用)

p-名称空间使您可以使用bean元素的属性(而不是嵌套 元素)来描述属性值和/或合作bean。

Spring支持带有命名空间的可扩展配置格式 ,这些命名空间基于XML模式定义。beans本章讨论的配置格式在XML Schema文档中定义。但是,p-namespace并未在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
        http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean name="classic" class="com.example.ExampleBean">
        <property name="email" value="foo@bar.com"/>
    </bean>

    <bean name="p-namespace" class="com.example.ExampleBean"
        p:email="foo@bar.com"/>
</beans>

该示例在bean定义中显示了名为email的p名称空间中的一个属性。这告诉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
        http://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 name="spouse" ref="jane"/>创建bean spouse引用jane,而第二个bean定义p:spouse-ref="jane"用作属性来完成同样的事情。在这种情况下spouse是属性名称,而该-ref部分表明这不是一个正值,而是对另一个bean的引用。
    p-名称空间不如标准XML格式那么灵活。例如,声明属性引用的格式与结尾的属性发生冲突Ref,而标准的XML格式则不会。我们建议您谨慎选择您的方法,并将其传达给您的团队成员,以避免生成同时使用所有三种方法的XML文档。

七、带有c-namespace的XML快捷方式

与带有p-命名空间的XML快捷方式类似,Spring 3.1中新引入的c-命名空间允许使用内联属性来配置构造函数参数,而不是嵌套constructor-arg元素。

1 回顾

  • 让我们回顾一下从例子中基于构造函数的依赖注入与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
        http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="bar" class="x.y.Bar"/>
    <bean id="baz" class="x.y.Baz"/>

    <!-- traditional declaration -->
    <bean id="foo" class="x.y.Foo">
        <constructor-arg ref="bar"/>
        <constructor-arg ref="baz"/>
        <constructor-arg value="foo@bar.com"/>
    </bean>

    <!-- c-namespace declaration -->
    <bean id="foo" class="x.y.Foo" c:bar-ref="bar" c:baz-ref="baz" c:email="foo@bar.com"/>

</beans>

2 c:命名空间

  • 该c:命名空间使用相同的约定作为p:一个(后-ref为bean引用),供他们的名字设置构造函数的参数。同样,即使它没有在XSD模式中定义(但它存在于Spring内核中),也需要声明它。
    对于构造函数参数名称不可用的罕见情况(通常如果字节码是在没有调试信息的情况下编译的),可以使用回退参数索引:
<!-- c-namespace index declaration -->
<bean id="foo" class="x.y.Foo" c:_0-ref="bar" c:_1-ref="baz"/>
  • 由于XML语法,_因为XML属性名称不能以数字开头(即使某些IDE允许),索引表示法也要求存在领先。
  • 实际上,构造器解析 机制在匹配参数方面非常有效,所以除非真的需要,否则我们建议在整个配置中使用名称符号。

八、复合属性名称

在设置bean属性时,只要最终属性名称以外的路径的所有组件都不是,就可以使用复合或嵌套属性名称null。考虑下面的bean定义。

<bean id="foo" class="foo.Bar">
    <property name="fred.bob.sammy" value="123" />
</bean>

该foobean有一个fred属性,该属性具有一个属性,该bob属性具有sammy 属性,并且该最终sammy属性被设置为该值123。为了使这一工作,fred财产foo和bob财产fred绝不能 null豆后构造,或NullPointerException抛出。

九、依赖使用

如果一个bean是另一个bean的依赖,那通常意味着一个bean被设置为另一个bean的属性。通常,您可以使用基于XML的配置元数据中的 元素完成此操作。但是,有时豆类之间的依赖性不那么直接; 例如,类中的静态初始化器需要被触发,例如数据库驱动程序注册。在depends-on使用此元素的bean被初始化之前,该属性可以显式强制一个或多个bean被初始化。以下示例使用该depends-on属性来表示对单个bean的依赖关系:

<bean id="beanOne" class="ExampleBean" depends-on="manager"/>
<bean id="manager" class="ManagerBean" />
  • 要表示对多个bean的依赖关系,请提供一个bean名称列表作为depends-on属性的值,用逗号,空格和分号作为有效分隔符:
<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-onbean定义中的属性可以指定一个初始化时间依赖关系,并且在只有singleton bean 的情况下可以指定一个相应的销毁时间依赖关系。定义depends-on与给定bean 的关系的依赖 bean在销毁给定bean之前首先被销毁。因此depends-on也可以控制关​​机顺序。

十、Bean的延迟加载机制

默认情况下,ApplicationContext实现急切地创建和配置所有的 singleton bean作为初始化过程的一部分。通常,这种预先实例化是可取的,因为配置或周围环境中的错误是立即发现的,而不是几小时甚至几天后。
如果你觉得上述的那种机制不太好,你可以将指定的bean的定义为延迟加载,这个豆就不会预加载实例化了。这个延迟加载的属性告诉容器,只有在第一次请求访问需要用到这个豆的时候才创建实例化这个豆,而不是在启动的时候。

  • 在XML中,此行为由元素lazy-init上的属性控制 的<bean /> 的延迟加载的属性; 例如:
<bean id="lazy" class="com.foo.ExpensiveToCreateBean" lazy-init="true"/>
<bean name="not.lazy" class="com.foo.AnotherBean"/>

当前面的配置被an消费时ApplicationContext,名为的bean lazy在ApplicationContext启动时并不急于预先实例化,而not.lazybean则被急切地预先实例化。

然而,当一个懒惰初始化bean是一个未经过延迟初始化的单例bean的依赖关系时 ,它ApplicationContext会在启动时创建延迟初始化bean,因为它必须满足单例的依赖关系。懒惰初始化的bean被注入到其他地方的单身bean中,而不是延迟初始化的。
您还可以通过使用元素default-lazy-init上的属性来控制容器级别的延迟初始化 <beans/>; 例如:

<beans default-lazy-init="true">
    <!-- no beans will be pre-instantiated... -->
</beans>

十一、相协作的Bean自动装配

Spring容器可以自动配置相互协作的bean,你可以允许Spring去解决不同bean直接的协作关系,通过ApplicationContext容器在启动的时候自动配置相协作的bean。

1.自动转配有哪些好处

  • 自动装配可以显著的减少一些需要配置的属性和构造。(其他的机制,像bean的模板方法,就是一个很好用的自动装配的方法)
  • 自动装配可以随着你的项目类容的更新,及时更新配置文件。
  • 例如,你想要添加一个bean A到另外一个bean B中去,让B依赖于A。A很容易的就成功自动的装配好了,不需要你再去定义配置。
  • 自动化装配依赖,在开发中非常有用,不用你再去辛苦的写Xml文件了。
  • 当使用的xml配置元数据的时候,你在定义的时候,给这个豆指定自动装配的属性。
  • 自动装配主要有四种模式,你可以选择一个适合你自己的。
模式 解释
No 不使用自动装配。默认是没有自动装配的属性的.Bean的引用必须通过 <ref /> 明确给出。如果是非常大型的醒目,不建议去设置成自动装配的,因为一些特别的协作豆,需要被分层清晰明确的给出。
byName 根据名字自动装配,Spring会查找同样的bean,根据这些bean的名字去确定那些是需要自动装配的。例如一个Bean被设置为需要根据名字自动装配,并且它包含主要的属性,它的名字是master,那么Spring就会去查找名字是master的bean,并且使用它去完成自动装配的工作。
byType 根据类型自动装配。如果容器中恰好存在一个类型的Bean A,有一个bean B被设置成自动装配的属性,恰好是A的类型,那么就可以完成自动装配。如果符合类型要求的在容器中不只有一个,那么就会抛一个致命的异常,它表明A不能使用这个类型做自动装配。如果容器中一个符合A自动装配的类型都没有,那么什么都不会发生,A自动装配的属性没有用了
constructor 根据构造器自动装配。 类似于根据类型装配,但是适用于构造器参数。但是如果容器中没有一个准确的符合要求的,就会报一个重大的错

2.几种自动注入模式的分析

当使用根据类型注入或者构造器自动注入的模式的时候,你可以注入数组和类型集合。在这种情况下,所有的需要依赖注入的候选bean,都由容器去匹配预期并提供合适的依赖bean。
如果要求的Key的主键是String类型的,你可以注入强类型的Map集合。
一个被装配的Maps集合,由所包含的Bean的实例组成,这个maps集合需要满足符合条件的类型,并且它的key要和要求的Key的值一致。
在自动装配完成后,你可以结合自动装配的行为做依赖检查。

3. 考虑一下自动自动装配的优点和缺点

  • 显式依赖关系property和constructor-arg设置总是覆盖自动装配。您不能自动调用所谓的简单属性,如基元 Strings,和Classes(以及这种简单属性的数组)。这个限制是通过设计。(就是说,如果你显示配置了和自动装配一样的属性,自动装配就会失效)。
  • 自动装配不如显示的准确装配。正如第1步的表格说述,Spring非常小心的去避免在一些表述不够准确的地方进行猜测,否则就会产生意想不到的错误结果,这样引起的问题就是,你的Spring管理的对象之间的关系就不再明确了。
  • 装配的信息可能无法用于Spring容器中的工具去生成文档。
  • 容器中的多个bean定义可以匹配由setter方法或构造函数参数指定的类型以进行自动装配。对于数组,集合或地图,这不一定是个问题。然而,对于期望单一值的依赖关系,这种不明确性不是任意解决的。如果没有唯一的bean定义可用,则抛出异常。

在上述最后的一种情况下,你有以下几种选择:

  • 放弃自动装配,而去支持显示装配。
  • 通过设置其autowire-candidate属性,避免为bean定义自动装配,false如下一节所述。
  • 通过将元素的属性设置为,将单个bean定义指定为主要候选者 。primary <bean/> true。
  • 如基于注释的容器配置中所述,实施基于注释的配置提供的更精细的控制。

4. 从自动装配中排除bean

在每个bean的基础上,您可以从自动装配中排除一个bean。在Spring的XML格式中,设置元素的autowire-candidate属性为false; 容器使该特定的bean定义对自动装配基础结构不可用(包括注释样式配置等@Autowired)。

该autowire-candidate属性旨在仅影响基于类型的自动装配。它不会影响按名称显式引用,即使指定的bean未标记为自动连线候选,也会得到解决。因此,如果名称匹配,通过名称自动装配将注入一个bean。

您还可以根据与bean名称的模式匹配来限制自动导向候选项。顶级元素在其default-autowire-candidates属性中接受一个或多个模式 。例如,要将autowire候选者状态限制为名称以Repository结尾的任何Bean ,请提供值* Repository。要提供多种模式,请在逗号分隔列表中定义它们。bean定义属性的显式值true或者false对于bean定义autowire-candidate属性的显式值 总是优先的,对于这样的bean,模式匹配规则不适用。

这些技术对于不想通过自动装配注入其他bean的bean非常有用。这并不意味着排除的bean本身不能使用自动装配进行配置。相反,该bean本身不是自动装配其他bean的候选者。

十二、方法注射

在大多数应用场景中,容器中的大部分bean都是 单例。当单例bean需要与另一个单例bean协作,或者非单例bean需要与另一个非单例bean协作时,通常通过将一个bean定义为另一个bean的属性来处理依赖。当bean生命周期不同时会出现问题。假设单例bean A需要使用非单例(原型)bean B,可能在A上的每个方法调用上。容器只创建一次单例bean A,因此只有一次机会来设置属性。每次需要时,容器都不能向bean A提供bean B的新实例。
解决方案是放弃一些控制反转。您可以通过实现接口来让bean A知道容器ApplicationContextAware,并且每当bean A需要时,通过对容器的getBean(“B”)调用请求(通常是新的)bean B实例。以下是这种方法的一个例子:

// a class that uses a stateful Command-style class to perform some processing
package fiona.apple;

// Spring-API imports
import 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容器的一个高级特性,它允许以干净的方式处理这个用例。

1. 查找方法注入

1.1 查找方法注入是容器覆盖容器管理的bean上方法的能力 ,以返回容器中另一个命名bean的查找结果。查找通常包含一个原型bean,如前一节所述。Spring Framework通过使用CGLIB库中的字节码生成来动态生成覆盖该方法的子类,从而实现了此方法注入。

为了使这个动态子类工作,Spring bean容器将继承的类不能成为final,并且被覆盖的方法也不能final。
对具有abstract方法的类进行单元测试需要您自己对该类进行子类化并提供该abstract方法的存根实现。
组件扫描也需要具体的方法,这需要具体的类来提取。
另一个关键的限制是查找方法不适用于工厂方法,特别是不适@Bean用于配置类中的方法,因为在这种情况下容器不负责创建实例,因此不能创建运行时生成的子类飞。
看一下CommandManager前面的代码片段中的类,你会发现Spring容器会动态地覆盖该createCommand() 方法的实现。您的CommandManager类将不会有任何Spring依赖关系,如在重新构造的示例中所示:

package fiona.apple;

// no more Spring imports!

public abstract class CommandManager {

    public Object process(Object commandState) {
        // grab a new instance of the appropriate Command interface
        Command command = createCommand();
        // set the state on the (hopefully brand new) Command instance
        command.setState(commandState);
        return command.execute();
    }

    // okay... but where is the implementation of this method?
    protected abstract Command createCommand();
}

2. 签名

在包含要注入的方法的客户机类(CommandManager在这种情况下)中,要注入的方法需要以下形式的签名:

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

3. 抽象方法

如果该方法是abstract,则动态生成的子类将实现该方法。否则,动态生成的子类会覆盖原始类中定义的具体方法。例如:

<!-- a stateful bean deployed as a prototype (non-singleton) -->
<bean id="myCommand" class="fiona.apple.AsyncCommand" scope="prototype">
    <!-- inject dependencies here as required -->
</bean>

<!-- commandProcessor uses statefulCommandHelper -->
<bean id="commandManager" class="fiona.apple.CommandManager">
    <lookup-method name="createCommand" bean="myCommand"/>
</bean>

4. @Lookup

标识为commandManager的bean createCommand() 在需要myCommand bean 的新实例时调用其自己的方法。你必须小心地将myCommandbean 部署为原型,如果这实际上是需要的话。如果它是单身人士,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注入点。检查Scoped bean作为依赖关系。

5.任意方法替换

与查找方法注入相比,不太有用的方法注入形式是能够用另一个方法实现来替换托管bean中的任意方法。在实际需要功能之前,用户可以安全地跳过本节的其余部分。
使用基于XML的配置元数据,您可以使用该replaced-method元素将现有的方法实现替换为已部署的bean。考虑下面的类,我们想要覆盖一个方法computeValue:

public class MyValueCalculator {

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

    // some other methods...
}

实现org.springframework.beans.factory.support.MethodReplacer 接口的类提供了新的方法定义。

/**
 * meant to be used to override the existing computeValue(String)
 * implementation in MyValueCalculator
 */
public class ReplacementComputeValue implements MethodReplacer {

    public Object reimplement(Object o, Method m, Object[] args) throws Throwable {
        // get the input value, work with it, and return a computed result
        String input = (String) args[0];
        ...
        return ...;
    }
}

部署原始类并指定方法覆盖的bean定义如下所示:

<bean id="myValueCalculator" class="x.y.z.MyValueCalculator">
    <!-- arbitrary method replacement -->
    <replaced-method name="computeValue" replacer="replacementComputeValue">
        <arg-type>String</arg-type>
    </replaced-method>
</bean>

<bean id="replacementComputeValue" class="a.b.c.ReplacementComputeValue"/>

您可以使用<arg-type/>元素中的一个或多个包含元素 来指示被覆盖的方法的方法签名。只有当方法过载并且类中存在多个变体时,参数的签名才是必需的。为了方便,参数的类型字符串可能是完全限定类型名称的子字符串。例如,以下全部匹配java.lang.String:

java.lang.String
String
Str

由于参数的数量通常足以区分每个可能的选项,因此只需键入与参数类型匹配的最短字符串,此快捷键就可以节省大量输入。

Hi好啦,目前官网关于IOC的详细的依赖和配置就讲了这么多东西。

相关实践学习
基于CentOS快速搭建LAMP环境
本教程介绍如何搭建LAMP环境,其中LAMP分别代表Linux、Apache、MySQL和PHP。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
8月前
|
XML 存储 设计模式
Spring高手之路11——BeanDefinition解密:构建和管理Spring Beans的基石
本文对BeanDefinition进行全面深入的探讨,涵盖BeanDefinition的接口方法、主要信息、类型以及生成过程等方面内容。旨在帮助读者全面理解BeanDefinition的各方面知识,并能够熟练应用。文章通俗易懂,具有很强的指导意义。
64 0
Spring高手之路11——BeanDefinition解密:构建和管理Spring Beans的基石
|
4月前
|
Java Spring
mapstruct的spring拓展
mapstruct的spring拓展
63 1
|
10月前
|
开发框架 前端开发 Java
【Spring】Spring框架介绍,功能模块,容器知识和有关Spring的生态圈的详细讲解
【Spring】Spring框架介绍,功能模块,容器知识和有关Spring的生态圈的详细讲解
98 0
|
9月前
|
XML Java 测试技术
Spring高手之路8——Spring Bean模块装配的艺术:@Import详解
本文将带你深入探索Spring框架的装配机制,以及它如何使你的代码更具模块化和灵活性。我们首先介绍Spring手动装配的基础知识,然后进一步解析@Import注解在模块装配中的关键角色。文章涵盖从导入普通类、配置类,到使用ImportSelector和ImportBeanDefinitionRegistrar进行动态和选择性装配等多个层次,旨在帮助读者全面理解和掌握Spring的装配技术。
235 0
Spring高手之路8——Spring Bean模块装配的艺术:@Import详解
|
10月前
|
XML 前端开发 Java
Spring高手之路2——深入理解注解驱动配置与XML配置的融合与区别
本文旨在深入探讨Spring框架的注解驱动配置与XML配置,揭示两者之间的相似性与差异。我们首先介绍了配置类的编写与Bean的注册,然后比较了注解驱动的IOC依赖注入与XML依赖注入。文章进一步解析了Spring的组件注册与组件扫描,包括使用@ComponentScan和XML启用component-scan的情况,以及不使用@ComponentScan的场景。接下来,我们深入探讨了其他相关的组件注册注解。最后,我们展示了如何将注解驱动的配置与XML驱动的配置结合使用,并通过思考总结部分提出了一些重要的问题
130 1
Spring高手之路2——深入理解注解驱动配置与XML配置的融合与区别
|
前端开发 Java 数据库连接
Spring模块与应用场景
Spring框架是一个Java平台,它为Java应用程序提供全面的基础框架支持。它也是现在非常流行的开源框架,只要是做Java开发,肯定接触过Spring的使用,不管我们对它的了解如何,多或者少,我们还是要尽力挖掘出Spring的功能价值。
232 1
Spring模块与应用场景
|
Java 程序员 容器
《Spring核心技术》第3章:深度解析@Bean注解
沉淀,成长,突破,帮助他人,成就自我。
121 0
《Spring核心技术》第3章:深度解析@Bean注解
|
Java 数据库连接 API
SpringFramework核心技术三:Spring的验证机制
Spring验证 Spring 3引入了对其验证支持的几项增强。首先,现在完全支持JSR-303 Bean验证API。其次,当以编程方式使用时,Spring的DataBinder现在可以验证对象并绑定到它们。
1902 0
|
Java Spring 数据格式
SpringFramework核心技术一(IOC:注册一个LoadTimeWeaver)
一、什么是LoadTimeWeaver 在LoadTimeWeaver用于由Spring动态变换的类,因为它们被装载到Java虚拟机(JVM)。
1961 0
|
Java Spring 容器
SpringFramework核心技术一(IOC:ApplicationContext的附加功能)
标题 正如本章介绍中所讨论的,该org.springframework.beans.factory 包提供了用于管理和操作bean的基本功能,包括以编程方式。
1124 0