03、IoCContainer-3 依赖

  1. depends-on的使用

    一般情况下,我们使用<ref/>元素来定义bean之间的依赖关系,但是有时候bean之间并不直接依赖。depends-on属性可以显式的强制一个或多个被依赖的beans在使用依赖的bean初始化之前被初始化。

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

    manager将在beanOne之前被创建。
    多个依赖:

    <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名称之间用,;或者是空格隔开。

    depends-on属性还会控制有依赖关系的bean的销毁顺序,上面的配置中,beanOne先被销毁,然后销毁manageraccountDao

  2. 懒加载bean

    ApplicationContext默认使用预加载,将bean的创建作为容器初始化进程的一部分。然而,我们可以通过lazy-init属性将bean设置为懒加载。

    <!-- lazy将会在初次需要是被创建 -->
    <bean id="lazy" class="com.something.ExpensiveToCreateBean" lazy-init="true"/>
    

    也可以在容器级别设置为懒加载:

    <beans default-lazy-init="true">
        <!-- 这里定义的bean全部为懒加载 -->
    </beans>
    
  3. 自动装配

    Spring容器可以自动装配合作bean的依赖关系。我们可以让Spring通过检测ApplicationContext容器的内容来达到自动解析依赖的目的。自动装配有一下优点:

    • 自动装配可以显著的减少配置bean属性或者是构造器参数的需要;
    • 当我们的对象改变时,自动装配可以更新配置。

    我们通过<bean>元素的autowire属性来设置自动装配,自动装配有4种模式:

    模式解释
    no(默认)不使用自动装配,bean之间的依赖必须使用ref来定义。在大型系统中,推荐使用该模式。因为明确的指定合作者可以带来更好的控制和清晰的系统结构。
    byName通过名字实现自动装配。Spring在容器中寻找与属性名相同的bean,并将其注入。(需要有对应的修改器方法)
    byType通过属性类型自动装配。如果容器中恰好有一个与属性类型匹配的bean,这个bean就会被注入。如果有多个匹配的bean,将会抛出一个重大的异常,该异常指示我们这个bean不应该使用byType模式。如果没有匹配的bean,将不会做任何操作。
    constructorbyType模式类似,只不过这里按类型将bean自动装配给bean的构造参数。匹配到多个或没有都将抛出异常。(在基于构造器的依赖注入中有效)

    自动装配的限制于缺点:

    如果要使用自动装配,那么最好整个项目都使用自动装配。仅仅在几个bean的定义中使用自动装配的话,可能会使开发人员混淆。

    • propertyconstructor-arg中明确指出依赖总是会覆盖自动装配。我们不能自动装配基本类型,字符串和数组这样的简单对象。
    • 尽管Spring在可能造成无法预测的结果的模糊情况下不会采用猜测的方式自动装配一个bean,自动装配也没有显式装配准确。
    • 那些从Spring容器中产生文档的工具无法获取装配信息。

    在以后的开发过程中,我们有一下几个选择:

    • 抛弃自动装配,使用显式装配
    • 设置autowire-candidatefalse,避免将该bean自动装配
    • <bean>元素的primary属性设置为true,使该bean成为首选被装入的bean
    • 使用基于注解的配置来实现更加细粒度的控制。

    在自动装配中,排除一个bean的注入:

    按类型自动装配模式中,可以将autowire-candidate的值设为false,这样,容器在自动装配时就会忽略这个bean。

    我们还可以在<beans>元素中,为defualt-autowire-candidate设置值,按bean的name进行样式匹配(patter-matching)。default-autowire-candidate的值可以是以,分开的列表。
    如我们要自动装配name以Repository结尾和Service结尾的bean,那么这个值就是*Repository,*Service

  4. 方法注入

    如果我们有一个单例beanA,需要调用多例beanB的方法,容器没有办法在A需要调用方法时,都提供一个最新的B实例。解决办法就是让A继承ApplicationContextAware接口,并调用getBean("B")方法,
    在每次A需要B时都从容器获取最新的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框架耦合了。方法注入(Method Injection) 这一Spring IoC容器先进的特性让我们解决这一用例更加干净利落。

    查找方法注入

    查找方法注入(lookup method injection) 让容器覆盖bean的方法,并将查找结果返回给另外一个bean。Spring框架通过CGLIB包的字节码生成工具来动态的生成一个子类,来实现或者覆盖返回查询结果的方法。

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

    因为容器会继承CommondManager,所以不能将该类声明为final;因为子类要实现或者覆盖createCommand方法,所以这个方法也不能声明为final。被注入的方法应该有这样的签名格式:

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

    配置信息:

     <!-- 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>
    

    commondManagerbean需要一个新的myCommand实例时,就调用自己的createCommand方法。

    与之对应的,基于注解的配置模式中,使用@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) {
            Command command = createCommand();
            command.setState(commandState);
            return command.execute();
        }
    
        @Lookup //不指明目标bean
        protected abstract Command createCommand();
    }
    

    任意方法替换:

    另外一种少见的方法注入形式:将一个bean的方法替换为里一个实现方法。

     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 {
    
             /**
              *obj - the instance we're reimplementing the method for
              *method - the method to reimplement
              *args - arguments to the method
              *Returns: return value for the method
              */
             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"/>
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值