Spring Document学习笔记——IoC容器(四)

依赖

自动装配协作者

Spring容器可以自动装配在协作bean之间的关系。

Spring可以根据ApplicationContext的内容自动分解出bean的协作者。

自动装配的优点:

自动装配可以显著减少指定属性或构造器参数的需要。

自动装配会在对象发生改变时自动更新配置,这样,就不需要反复改动已经写好的配置文件。

当使用基于xml的配置元数据时,通过<bean>元素的autowire属性指定自动装配模式。在使用时,为每一个bean指定一种自动装配模式。

自动装配有四种模式

no:(默认)不进行自动装配,bean引用必须通过ref元素进行定义。不建议在大型系统中使用自动装配,因为显式指定协作者可以使系统更可控清晰。

byName:通过属性名称进行自动装配。即bean的名称与属性名称一一对应。

byType:如果正好只有一个与属性同类型的bean存在于容器中,允许属性被自动装配。如果同类型bean存在多个会抛出严重异常,如果没有一个bean被匹配,那么这个属性不会被设置,也不会抛出异常。

constructor:与byType类似,但是应用于构造器参数。如果没有一个bean与构造器参数的类型匹配,那么一个重大错误会发生。

可以通过byType与constructor自动转配模式装配数组或集合。所有匹配期待类型的bean都会作为依赖注入。

可以将依赖检测与自动装配行为结合。

自动装配的缺点与局限

如果自动装配只用于部分bean,会使开发者陷入困惑。

自动装配的缺点如下:
property与constructor-arg的显式依赖设置将会重载自动装配。并且原始类型不能被自动装配。

自动装配没有显式装配明确。

装配信息不能用于生成文档。

多个bean定义匹配基于byType、constructor的自动装配指定的类型。对与数组、集合、Maps,这并不是问题,但是如果期待的依赖为单一值,那么将会抛出异常。

当最后一个场景发生时,用于应对的选项有:
放弃自动装配,选择显式装配。

通过将bean定义的autowire-candidate属性设置为false避免自动装配。

通过设置当前bean的primary属性,将一个单独bean定义指定为主要候选者。

通过基于注解的配置进行更细粒度的控制。

将一个bean排除出自动装配

可以通过将autowire-candidate属性设置为false,使得这个bean不能作为依赖被用于自动装配。

autowire-candidate属性只会影响基于类型的自动装配,并不会影响基于名称的显式引用。

所以,如果名称匹配的话,基于名称的自动装配会成功。

可以通过bean名称的匹配模式限制byName的自动装配。

<beans>的default-autowire-candidate属性可以接受多个模式。

eg:aaa*用以匹配名称以aaa为开头的bean。

可以通过逗号间隔提供多个匹配模式。

如果bean定义的autowire-candidate属性被显式设置为true或false,此bean会被优先匹配。但是,模式匹配规则不会被应用。

一个排除出自动装配的bean自身能够使用自动装配,但是不能被自动装配进其他beans中。

方法注入

bean有不同的生命周期,当不同生命周期的bean彼此依赖,并进行依赖注入时会产生问题。

eg:单例bean A被原型bean B注入,由于A只会被初始化一次,所以B只会被注入一次,B不会在每次被需要时都注入A一次。

一个解决方案是放弃部分控制反转,通过ApplicationContextAware接口令A获得ApplicationContext的引用,然后在每次需要B的bean时,使用getBean方法获取B的bean。

不过,由于与spring API的耦合,所以并不推荐。

查阅方法注入

查阅方法注入是容器重载容器管理的bean上方法的能力,用于返回使用其他bean名称查找的结果。

Spring框架通过CGLIB库的字节码生成技术动态生成重载方法的子类实现方法注入。

被方法注入的bean不能为final类,而且被重载的方法也不能为final。

单元测试被方法注入的抽象方法时,需要手动提供一个抽象方法的实现。

对于组件扫描来说,具体的方法是必要的。

查阅方法的更关键的局限在于不能使用工厂方法,特别是在配置类中使用的@Bean方法。

package fiona.apple;

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();
    }

    protected abstract Command createCommand();
}
<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>

note:使用方法注入,使得createCommand()方法返回Command bean。

public abstract class CommandManager {

    public Object process(Object commandState) {
        Command command = createCommand();
        command.setState(commandState);
        return command.execute();
    }

    @Lookup("myCommand")
    protected abstract Command createCommand();
}

note:使用基于注解的方式进行方法注入。

public abstract class CommandManager {

    public Object process(Object commandState) {
        MyCommand command = createCommand();
        command.setState(commandState);
        return command.execute();
    }

    @Lookup
    protected abstract MyCommand createCommand();
}

note:基于注解的方法进行方法注入,并且使用被重载的方法的返回值类型进行bean匹配。

ps:通常需要声明一个具有具体实现的被注解的查阅方法的stub,为了与Spring的组件扫描兼容,因为Spring组建扫描会忽略抽象类。

另一种访问不同范围的bean的方法是,使用ObjectFactory/Provide 注入点。

任意方法替换

一种比查阅方法注入更少使用的方法注入是替换被管理bean的任意方法的能力。

使用基于xml的配置元数据,可以通过replaced-method元素来替换一个已经存在的方法实现。

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

public class MyValueCalculator {

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

    // some other methods...
}
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 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"/>

ps:使用一个或多个包含<arg-type>元素的<replaced-method>元素指定被重载方法的方法签名。

ps:通常,只有当方法被重载并且存在多个变体于当前类中时,方法签名中的参数是必要的。

ps:方法签名中参数的类型string可以是完整类型名称的部分,eg:String映射java.lang.String。

ps:由于方法参数的数量往往就足以区分方法,所以可以使用类型的简写省略打入的字符数量。

学习源:Spring官方文档

猜你喜欢

转载自blog.csdn.net/qq_32165041/article/details/82350719