spring framework 4 学习之路 4 -- 依赖

       一个典型的企业级应用不会只包含一个对象。即使是最简单的应用也有一些类相互工作来呈现给终端用户。本节主要介绍如何将独立定义的beans组装成一个可用的应用。这个应用需要各个类相互协作来达到目标。

1.依赖注入

       依赖注入是指对象依赖的其他对象,只能通过构造器参数,工厂方法的参数,当对象初始化完后调用set方法或者从工厂方法返回等进行注入。容器在创建bean的时候注入它的依赖对象。这个过程是反转的,因此称为控制反转。由bean自己控制依赖的位置和初始化方式。

       在依赖注入原则下代码是干净的,复制是高效的。对象不需要查找他们的依赖,不需要知道依赖的类或者位置。因此,你的类会变得更容易测试,特别在依赖是接口或者抽象基类的时候,允许你在单元测试的时候mock实现。

       依赖注入存在两种主要变体,基于构造器的依赖注入和基于Setter依赖注入。

1.1 基于构造器的依赖注入       

        基于构造器的依赖注入是通过容器对每个依赖设置参数调用调用构造器方法来实现的。指定特定的参数调用bean的构造器方法和调用一个静态的工厂方法是类似的。对于构造器和对于静态工厂方法,指定的参数也是类似的。以下的例子展示了一个类只能被构造器进行依赖注入。注意这个类并没有任何特别的地方,它只是一个POJO,并不依赖任何的容器特定接口,基类或者注解。


1.2 构造器参数解决方案

        构造器参数解决方案会根据参数的类型进行匹配。如果bean定义中构造器参数不存在任何歧义的话,bean定义中构造器参数的顺序也对应于构造器的参数顺序。考虑如下的类:


        假定类Bar和类Baz没有任何继承关系,那么没有潜在的歧义存在。因此如下的配置可以很好的工作,你不需要在元素<constructor-arg/>中显示声明构造器参数索引或者类型。


       当另一个bean被引用时,类型是已知的,匹配就会发生,如上述例子所述。当一个简单的类型被使用,比如<value>true<value/>,Spring并不能决定这个值的类型,因此在没有任何帮助的情况下无法匹配类型。如下所示的例子:

          在上述的场景中,如果你通过type属性显式声明构造器参数,那么容器可以对这些简单类型使用类型匹配。如下所示:


         你也可以使用index属性来显式声明构造器参数位置,如下所示:


         除了解决多个简单类型的歧义外,声明索引也可以解决构造器中有两个相同类型参数的情况。注意index是从0开始的。

         你也可以通过显式声明构造器参数名称来解决值的歧义问题。如下所示:


         注意,为了使以上的工作可以开箱即用(out-of-box),你的代码必须在debug标志为可用的情况下被编译,这样Spring可以从构造器中查询参数名称。如果你不能在debug标志下编译代码,或者你并不想这样编译代码,你也可以使用JDK注解@ConstructorProperties来显式的命名构造器参数。简单的示例如下:


1.2 基于setter的依赖注入

       基于setter的依赖注入通过调用setter方法来完成依赖注入,setter方法的调用是在调用无参数构造器或者无参数静态工厂方法完成bean初始化后触发的。

       以下的例子展示了类只能使用setter注入来完成依赖注入。这个类是Java编写的。这是一个POJO类,并不依赖任何容器接口,基类或者注解。


       ApplicationContext对于它所管理的bean支持基于构造器或者基于setter的依赖注入。它同样允许针对已经通过构造器注入的依赖再次使用setter依赖注入。你可以通过BeanDefinition形式配置依赖,结合PropertyEditor实例将属性从一种形式转换成另一种形式。但是,大多数Spring用户并不直接与这些类打交道,而是使用XML的bean定义,组件注解或者在基于@Configuration注解的Java类中使用@Bean方法。这些配置都会转换成BeanDefinition实例,然后加载到整个Spring IoC容器实例里。

       到底应该选择基于构造器的依赖注入还是基于setter的依赖注入呢?由于你可以混合使用基于构造器的依赖注入和基于setter的依赖注入,因此最好的方式是针对强制依赖使用构造器依赖注入,对于可选的依赖选择setter方法或者配置方法。你可以在setter方法上使用@Required注解来将该属性变成强制依赖。

       Spring团队主张构造器依赖注入,因为它使得开发者可以实现应用组件为不可变对象,确保必须的依赖不为null。而且,基于构造器注入的组件常常返回给客户端是完全初始化状态。但是,值得一提的是,拥有大量入参的构造器是"坏代码味道",暗示者这个类有太多职责,应当做拆分。

       基于setter的依赖注入应当只为那些可选依赖服务,这些可选依赖可以在类中分配合适的默认值。否则,任何使用该依赖的代码处都应该有不为空检查。一个使用setter依赖注入的好处是你可以重新配置或者重新注入类的对象。JMX MBeans的管理就是以上述形式使用setter注入的例子。

       在没有源码的情况下处理第三方类库时,选择就明显了。比如,当第三方类库并没有暴露任何的setter方法,那么构造器依赖注入就是唯一的选择了。

1.3 依赖解决过程

       容器处理bean依赖过程如下:

       1.基于所有beans的配置元数据,创建并初始化ApplicationContext。配置元数据可以通过XML, Java code以及注解来声明;

       2.对每一个bean, 使用属性,构造器参数,静态工厂方法参数形式来表达依赖;当bean真正被创建的时候,这些依赖会提供给bean;

       3.每一个属性或者构造器参数实际上是待设置的值定义或者是指定容器的另一个bean;

       4.每一个属性或者构造器参数从特定的形式转换成属性或者构造器的实际类型。默认情况下,Spring可以转换string形式的值到任何内建对象,比如int, long, String, boolean等等。

       当容器创建时,Spring容器会验证每一个bean的配置。但是配置属性只有当bean真正被创建的时候才会进行设置。Beans都是单例的,设置为提前初始化。当容器被创建的时候bean也会被创建。否则,bean只会在需要的时候被创建。一个bean的创建会导致beans的图谱被创建,因为它的依赖,以及依赖的依赖都会被创建和分配。依赖解决过程中,依赖匹配错误会延迟展示。

        如果你使用构造器依赖注入,有可能会产生不可解决的循环依赖场景。比如:类A需要类B的实例,通过构造器注入,类B同样通过构造器注入类A的实例。如果你配置类A和类B的bean注入彼此,那么Spring IoC容器会在运行时探测到循环引用,并抛出异常BeanCurrentlyInCreationException。解决此问题的方法之一就是对于某些类使用setter进行配置,而不是构造器配置。当然你也可以禁止构造器注入,只允许setter注入。尽管这种方式不被推荐,但是你可以通过setter注入配置循环依赖。不同于典型的案例,A和B之间的循环依赖强制一个bean在它被完全初始化之前被注入到另一个bean中(一个典型的先有鸡还是先有蛋问题)。

        你可以相信Spring能够做正确的事情。它可以探测配置问题,比如引用不存在的beans以及循环依赖等,在加载容器时候。Spring尽可能迟的设置属性,解决依赖,直到bean被创建。这意味着一个已经加载正确的Spring容器稍后可能会抛出异常,如果你请求一个对象,但是这个对象创建失败或者依赖注入失败。比如,bean由于缺失属性或者无效属性会抛出异常。部分配置内容的延迟可见性也是ApplicationContext使用提前初始化单例beans的实现原因。通过花费一些提前预付的时间和内存,这些beans会在真正需要之前被创建,这样在ApplicationContext创建的时候,配置内容可见。你也可以重载默认的行为使得单例bean懒加载,而不是提前初始化。

        如果循环依赖问题不存在,当一个或者更多协作beans被注入到一个独立的bean中时,每一个协作bean已经在注入之前完全配置化了。这意味着如果bean A依赖bean B, Spring IoC容器会在调用bean A的setter方法之前就已经配置好B了。换言之,bean被初始化,它的依赖会被设置,相关的范围方法会被调用。

        以下展示基于XML配置的setter依赖注入示例:



        以下展示基于构造器依赖注入的示例:



       接下来,展示基于静态工厂方法来返回对象实例的配置示例:



       通过工厂方法返回的对象类型并不一定要和包含静态工厂方法的类保持一致,尽管以上的例子是这样实现的。

2. 依赖和配置详细介绍

       正如前文所述,你可以定义bean的属性和构造器参数引用其他beans,或者是确定的值。基于XML的配置元数据通过子标签<property/>和<constructor-arg/>来实现此目标。

2.1 直接定义值(原子类型,字符串等等)

       <property/>标签的value属性以字符串形式声明了属性或者构造器参数的值。Spring的conversion service用来将这些字符串表示的值转换成属性或者参数实际的类型值。


       下面的例子使用p-namespace来提供更加简洁的配置:


       上述的XML配置十分简洁,但是,错误的书写只会在运行时发现,而无法在编辑期间发现,除非你使用类似于IntelliJ IDEA或者Spring Tool Suite这种编辑器,因为这种编辑器能够在创建bean定义的时候自动属性校验。

       你也可以配置一个java.util.Properties实例,如下所示:


       Spring容器会转换<value/>标签里的文本到java.util.Properties实例,通过使用JavaBeans的PropertyEditor机制。

2.2 idref标签

       idref标签是一个简单的误差校验方式,将容器中另一个bean的id(字符串值,而不是引用)传递给<constructor-arg/>或者是<property/>标签。


       上述的bean定义和一下的配置方式是等价的:


        第一种方式的配置优于第二种配置方式,因为idref标签允许容器在开发阶段校验所引用的bean是否是存在的。在第二种方式中,id为client的bean的targetName属性值不会做任何的校验,错误校验只会在bean被创建的时候发生。如果client bean是一个prototype bean,那么错误校验和引起的异常只会在容器被加载后的很长一段时间内被发现。

        <idref>标签常用的一个地方是在ProxyFactoryBean的bean定义的AOP解释器的配置中。当你声明解释器名称时,使用<idref>标签能够阻止你错误拼写了一个解释器的id。

2.3 其他bean的引用

       ref标签在<constructor-arg/>或者<property/>标签中是十分关键的。你可以通过此标签设置bean的属性引用容器中管理的另一个bean。被引用的bean是当前bean的一个依赖,可以通过setter方法注入,并且该bean会在依赖注入之前被初始化。如果依赖的bean是单例bean,那么它会被容器初始化。所有的引用都是指向另一个对象。范围和校验依赖于你通过bean,local或者parent属性来声明对象的id/name。

       通过<ref/>标签的bean属性来声明目标bean是最常用的形式,允许在相同的容器或者父母容器中创建指向任意bean的引用,而不管是否是在相同的XML文件中定义的。bean属性的值可以和目标bean的id属性相同,或者是目标bean的其中一个name值。

<ref bean="someBean"/>

       通过parent属性声明目标bean会在当前容器的父母容器中创建引用的bean。parent属性的值可以和目标bean的id属性值相同,也可以是目标bean的众多name值中的一个。目标bean必须在当前容器的父母容器中。当应用存在多个分层的容器,并且你希望通过一个代理(代理bean的名称和服务bean名称相同)包装父母容器中已经存在的bean时,你也可以使用bean引用变体。如下所示:


2.4 内部bean

        在<property/>或者<constructor-arg/>标签内部定义的<bean/>称为内部bean。


       内部bean定义不需要指定id或者name。即使你声明了,容器也不会使用声明的值作为一个标识符。容器同样会在创建的时候忽略scope标签:内部beans都是匿名的,并且都是由外部bean所创建。一般情况下不会注入内部bean到协作bean中,也不会单独的获取它们。

       在临界情况下,有可能从custom的范围里获取销毁回调,比如包含在单例bean中的request范围的内部bean:内部bean的创建与包含它的容器相关,但是销毁回调允许它参与request范围内的生命周期。这不是一般的场景;一般情况下,内部bean和包含它的bean共享范围。

2.5 集合

       通过<list/>, <set/>, <map/>以及<props/>标签,你可以使用Java集合类型List,Set, Map以及Properties来设置属性或者参数。


         map的key或者value, set的value值可以使用以下任意的标签:


2.6 集合联合使用(Collection merging)

       Spring容器也支持集合的联合使用。应用开发者可以定义父标签<list/>, <map/>, <set/>或者<props/>,同时在这些父标签下定义子标签<list/>, <map/>, <set/>或者<prop/>, 这些子标签可以重载父标签集合的值。也就是说,子集合标签的值是合并父标和子标签集合的结果,因为子集合标签可以重载父集合标签声明的值。

      以下的例子说明了集合联合使用的情况:


       注意到id为child的bean定义中,属性adminEmails的<props/>标签上merge=true的使用。当id为child的bean被容器初始化时,属性adminEmails的Properties集合包含了id为child的adminEmails集合和id为parent的adminEmails集合合并后的结果数据。

administrator=administrator@example.com
sales=sales@example.com
support=support@example.co.uk
        child bean的Properties集合继承了所有parent bean的<props/>值,同时,child bean的support值重载了parent bean中的值。

        以上的merging行为同样适用于<list/>, <map/>, <set/>集合标签。但是,对于<list/>标签而言,它需要保持一个有序集合的概念,父标签的值优先于所有子标签的值。在Map, Set, Properties集合类型中,并不存在有序的概念。因此,无序的语义适用于容器使用Map, Set, Properties类型设置的属性。

       你不能联合使用不同的结合类型,比如混合使用Map和List。如果你这样做的话,系统会抛出对应的异常。merge属性只能用在child bean的定义中,在parent bean中定义merge属性是多余的,并不会达到你想要的结果。

2.7 Strongly-typped集合

       Java5之后引入了泛型,因此你可以使用强类型集合,也就是说,你可以声明一个Collection类型,该集合只能包含String元素。如果你使用Spring来注入一个强类型Collection到bean中,你可以好好利用Spring的类型转换支持,它可以将强类型Collection实例中的元素转换成合适的类型,在被加入到Collection集合之前。


       当foo bean的accounts属性准备注入的时候,属性为强类型Map<String, Float>的泛型信息可以通过反射获取。Spring的类型转换架构会识别类型Float的多种值,将字符串值9.99,2.75,3.99转换成实际的Float类型。

2.8 Null和空串值

       如下基于XML的配置元数据片段设置email属性为空串。


       上述的例子类似于Java代码:exampleBean.setEmail("");

       <null/>标签处理null值,如下所示:


       上述的配置类似于Java代码:exampleBean.setEmail(null);

2.9 使用p-namespace简化XML配置

        p-namespace使得开发者可以使用bean元素的属性,而不用内嵌的<property/>标签,来描述属性值或者是协作beans。

        Spring通过namespaces支持可扩展的配置格式。namespaces是基于XML的模式定义。这一节讨论的beans配置格式在XML的模式文档里定义了。但是,p-namespaces并不在一个XSD文件中定义,只存在于Spring中。

      下述的例子展示了两个XML配置片段,这两个配置的效果是一样的,前者使用标准的XML配置格式,或者使用了p-namespaces.


       上述的例子展示了p-namespace的email属性在bean中的定义。这就告诉了Spring引入属性定义。正如之前所提到的,p-namespace并没有模式定义,因此你可以设置属性的名称就是property的名称,如p:email中的email就是属性email的名称。

        下面的例子展示了引用其他bean的两种定义方式:


        正如你所看到的,上述的例子不仅包含使用p-namespace定义属性值,同时也包含了使用特定格式来声明属性的引用。不同的是,第一个bean定义中使用<property name="spouse"  ref="jane"/>来创建引用jane bean,第二种方式使用p:spouse-ref="jane"来做相同的事情。在第二种bean定义中,-ref部分表示这不是一个直接的值,而是指向其他bean的引用。

2.10 使用c-namespace来简化XML配置

         与p-namespace类似,c-namespace,由Spring3.1引入,允许使用内嵌的属性来配置构造器参数,而不是使用construtor-arg标签。

          以下的例子展示了通过c: namespace来实现基于构造器依赖注入:


       c: namespace使用和p:相同的方式来设置构造器参数,通过参数的名称。对于少数情况下,构造器参数的名称可能无法获取,这种情况下可以使用参数index来表示,如下所示:

      实际上,构造器解决机制就是参数匹配,除非你真的需要这么多,我们推荐在整个配置中使用名称注解,而不是index标识。

2.11 复合属性名称     

      你也可以在设置bean属性的时候使用复合属性名称,只要所有路径上的组件,除了最终的属性名称,都不允许为null。如下例所示:

       bean foo有一个fred属性,fred有一个bob属性,bob有一个sammy属性。最后的sammy属性会被设置成123。为了使这个配置能够正常工作,foo的fred属性,fred的bob属性不能为null,在bean被构造的过程中,否则会抛出空指针异常。

3. 使用depends-on

       如果一个bean是另一个bean的依赖,也就是说,这个bean是另一个bean的属性。一般情况下,你可以在XML配置元数据中使用<ref/>标签来完成依赖注入。但是,有时候beans之间的依赖并不是直接的。比如:类中的静态初始化器需要被触发,如数据库驱动注册。depends-on属性可以明确地强制一个或者多个beans的初始化动作在使用这些beans的bean初始化之前完成。以下的例子使用depends-on属性来表达在单个bean上的依赖:


       为了表达一个bean依赖多个beans,可以在depends-on属性上以逗号,空格或者分号作为有效的分隔符来列出所有的bean名称。如下所示:


      bean定义中的depends-on属性不仅可以在初始化时间声明依赖,在单例beans中,也可以在销毁时间段声明依赖。通过depends-on定义的依赖beans会首先被销毁,早于给定的bean它自身被销毁的时间。因此,depends-on也可以控制关闭的顺序。

4.懒初始化beans

      默认情况下,ApplicationContext实现会将创建和配置所有单例beans作为初始化的一部分。一般地,beans都是提前初始化,因为这样可以尽早地发现配置或者环境方面的错误,而不是等到几小时或者几天后才发现。当你不希望单例bean被提前初始化时,你可以标记该bean定义为懒初始化。一个懒初始化bean告诉IoC容器只有当它被请求时才会创建bean的实例,而不是在启动阶段。

       在XML中,你可以通过<bean/>标签上的lazy-init属性来控制懒初始化行为,如下所示:


       当上述的配置被ApplicationContext识别后,名称为lazy的bean并不会在ApplicationContext启动的时候被提前初始化,但是名称为not.lazy的bean会被提前初始化。

       但是,当一个懒初始化bean是一个单例bean的依赖时,而且这个单例bean并不是懒初始化的,那么ApplicationContext会在启动时创建懒初始化bean,因为单例bean的依赖必须在单例bean初始化之前被初始化。一个懒加载bean被注入到单例bean中就不再是懒加载模式了。

       你也可以在容器级别控制懒初始化,通过在<beans/>标签上使用default-lazy-init属性,如下所示:


5. 自动装配协作者

       Spring容器可以自动装配协作beans之间的关系。你可以让Spring自己解决bean的协作者,通过检查ApplicationContext的内容。自动装配有以下几点优势:

       1.自动装配可以明显地减少声明属性或者构造器参数的必要;

       2.当你的对象不断变化时,自动装配可以更新配置。比如,如果你需要给一个类增加一个依赖,那么这个依赖可以被自动装配而不需要你修改配置。因此,自动装配在开发阶段是十分有用的。

       当你使用基于XML配置元数据时,你可以为bean定义声明自动装配方式,通过在<bean/>标签上使用autowire属性。自动装配功能有4中方式。


       使用buType或者构造器装配方式,你可以装配数组和集合类型。在这种情况下,容器中的所有装配候选者匹配期望的类型,提供给对应的依赖。你可以装配强类型Maps,如果对象的key类型是String。一个装配的Maps值包含匹配对应类型的所有bean实例,Maps的keys会包含相关的bean名称。

       你可以结合装配行为和依赖检查,依赖检查是在装配完成后进行的。

       当在整个项目中都使用自动装配方式,那么自动装配可以很好的工作。但是,如果并非如此,那么就会让开发者产生疑惑,到底是装配了一个还是两个bean定义。自动装配有如下的缺点和限制:

      1. 在property和constructor-arg设置的明确依赖会重载自动装配的结果。你不能自动装配简单的属性,比如原始类型,Strings以及Classes(也包含数组)。

       2.自动装配没有显式链接明确。尽管,在上述表格中提到的,Spring很小心的避免猜测以防歧义,这些歧义会导致不可预估的结果,但是,Spring管理的对象之间的关系不再是通过文档明确记录的。

       3.装配信息可能对工具来说是不可获取的,这些工具会从Spring容器中生成文档。

       4.容器中的多个bean定义可能会匹配需要自动装配的setter方法或者是构造器参数的类型。对于数组,集合,或者Map,这都不是问题。但是,当依赖是一个单独的值,歧义就不是很好解决。如果没有唯一的bean定义,那么就会抛出异常。

       在稍后的场景中,你会有其他的选择:

       1.放弃自动装配,支持显式的手写配置;

       2.通过设置autowire-candidate属性为false来避免自动装配这个bean的定义;

       3.通过设置<bean/>标签的primary属性为true来指定单个bean定义为唯一候选者。

       4.通过基于注解的配置,实现更细粒度的控制。

       对于每个bean,你可以使其不被自动装配。在Spring的XML格式中,设置<bean/>标签的autowire-candidate属性为false,这样容器会让对应的bean不被自动装配架构识别(也包含注解形式的配置@Autowired)。

       autowired-candidate属性只会影响基于类型的自动装配。它不会影响通过名字进行的显式引用。即使声明的bean没有被标记为自动装配候选,它也能被发现。因此,只要名称匹配,通过名称进行自动装配方式就会注入该bean。

       你也可以通过模式匹配bean的名称来限制自动装配的候选者。<beans/>标签通过它的default-autowire-candidates属性接受一个或者多个的模式。比如,为了限制自动装配的候选者名称必须以Repository结尾,那么可以设置default-autowire-candidates属性值为*Repositoy。如果需要提供多个模式,可以用逗号分隔符进行列出。如果bean定义使用了autowire-candidate属性设置了true或者false,那么autowire-candidate属性值优先,模式匹配的规则就不再适用。

        如果你希望某些beans不被自动装配注入到其他beans中,那么这些技术都是有用的。这并不意味着一个被排除掉的bean不能再使用自动装配配置了。它只是声明这个bean不会成为自动装配的其他beans的候选者而已。

6. 方法注入

       在大多数应用场景中,容器中的大多数beans都是单例的。当一个单例bean需要和其他单例bean协作时,或者一个非单例bean需要与其他非单例bean协作时,通常情况下,你会通过定义一个bean作为另一个bean的属性来处理依赖。当bean的生命周期不同时,问题就会出现了。假定单例bean A需要使用非单例(prototype)bean B, 可以在A上进行方法调用。容器会立刻创建单例A, 因此只有一次机会设置属性。容器不能提供bean A一个新的bean B实例,在每次需要的时候。

        一种解决方案就是放弃一些控制反转。你可以通过实现ApplicationContextAware接口来使得bean A感知容器,通过调用容器的getBean("B")方法来获取bean B的实例,当每次Bean A需要Bean B的时候。一个例子如下所示:


      上述的实现方式是不推荐的,因为业务代码感知Spring Framework,并且与框架紧密耦合。方法注入,Spring IoC容器的一大特点,能够以更加干净的方式解决上述问题。

       查询方法注入是容器的能力,用来重载容器管理的beans的方法,返回容器中另一个命名bean的查询结果。这个查询一般涉及的是prototype bean,如上述所属的场景中。Spring Framework实现方法注入通过使用从CGLIB生成的字节码来生成动态子类,该子类重载方法。

       为了使动态子类能够很好的工作,Spring容器用来生成子类的基类不能为final, 待重载的方法也不能为final;若要单元测试包含有abstract方法的类,你需要实现该类的子类,提供abstract方法的具体实现;具体的方法实现对于组件扫描也是必要的,组件扫描需要获取具体的实现类;另一个限制是查询方法不能和工厂方法一起工作,特别是不能和@Bean方法一起工作,因为在这种情况下容器并不负责实例的创建,因此不能创建运行时生成的子类。

       再看下上面代码片段中引入的CommandManager类,你可以看到Spring容器会动态重载createCommand()方法的实现。类CommandManager并不依赖任何Spring层面的代码,如下例子所示:


      在包含方法注入的客户端类中,被注入的方法需要如下形式的声明:


      如果方法是abstract,动态生成的子类需要实现该方法。否则,动态实现的子类会重载在父类具体实现了的方法。如下所示:


      当它需要使用myCommand bean的实例时,bean commandManager会调用它自己的方法createCommand()。你必须小心的部署myCommand bean作为prototype。如果它被设置为单例,那么myCommand bean每次返回的都是相同的实例。

      在基于注解的组件模型中,你可以通过@Lookup注解声明一个lookup方法,如下所示:

      或者,更地道地,你可以在目标bean上获取解决方法,通过在lookup方法上声称返回类型,如下所示:


       一般地,你会用一个具体的实现来声称基于注解的lookup方法,为了与Spring的组件扫描规则保持一致,因为组件扫描默认会忽略抽象类。这种限制不会应用在显式的注册或者显式的引入bean类。

       一个相对于lookup方法注入使用较少的方法注入形式是通过另一个方法的实现替换掉所管理的bean的方法。用户可以放心的忽略掉这节剩余部分的内容,直到你确实需要使用这个功能。

      在基于XML配置元数据中,你可以使用replaced-method标签来替换一个已经存在的方法实现,对于一个已经部署了的bean。考虑下面的类,拥有一个computeVaue方法,我们想重载这个方法:


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


      部署原始类的bean定义以及声明重载方法的方式如下所示:


      你可以在<replaced-method/>标签下使用一个或多个<arg-type/>标签来指定被重载方法的参数签名。如果方法被重载了,类中存在多个变量,那么参数签名是有必要的。为了方便,参数类型为string可以是类型名称的子串。在下面的例子中,以下所有的表示都是匹配java.lang.String:


       由于参数的数量足够区分每一种可能的选择,简写能够节省很多的书写工作,通过允许开发者输入最短的字符串来匹配对应参数的类型。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值