Spring之方法注入(lookup method)

Spring使用CGLIB的动态字节码增强功能,所以,必须要加入CGLIB包

当Bean依赖另一个生命周期不同的bean,尤其是当singleton依赖一个non-singleton时,常会遇到不少问题,Lookup Method Injection正是对付这些问题而出现的,在上述情况中,setter和构造注入都会导致singleton去维护一个non-singleton bean的单个实例,某些情况下,我们希望让singleton bean每次要求获得bean时候都返回一个non-singleton bean的新实例

当一个singleton bean A 在每次方法调用的时候都需要一个non-singleton bean B,此时就会产生这样一个问题,因为A为singleton,所以容器只会创建一次A,那么也只有一次机会来创建A的属性,Bean B也只能被初始化一次,但是我们调用每个方法的时候又都需要一个新的B的实例。通常的时候我们只要new一个就可以,但在Spring中这不符合整体性的设计,这样就有了方法注入。
       让我们开始一个简单的测试。
       在你的工程的根目录中分别建立一个src,classes,lib文件夹。此次我们将采用Ant来进行部署。
       在src中建立Random.java,HelloRandom.java,HelloAbstract.java,Test.java。代码如下。
       Random.java这是一个可以产生随机数的类,很简单。

    package com.testspring.basic.lookup;
       public class Random {
               private int num = (int)(100*Math.random());
               public void printRandom(){
                   System.out.println("随机数是"+num);
               }
        }
     
    HelloRandom.java一个代理接口。

     package com.testspring.basic.lookup;
    public interface HelloRandom {
        public Random getRandom();
        public abstract Random createRandom();    //这个方法最为重要,是方法注入的关键
    }
     
    HelloAbstract.java

     package com.testspring.basic.lookup;
    public abstract class HelloAbstract implements HelloRandom{
        private Random random;
        public void setRandom(Random random){
              this.random = random;
        }
        public Random getRandom(){
            return this.random;
         }
        public abstract Random createRandom();
    }
     
       Test.java用于测试

    package com.testspring;
       import org.springframework.context.ApplicationContext;
       import com.testspring.basic.lookup.*;
       public class Test {
           public static void main(String[] args) {
             try{
                  ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
                  HelloRandom helloRandom = (HelloRandom)context.getBean("helloRandom");
                    System.out.println("下面两个实例没有采用方法注入");
                    Random r1 = helloRandom.getRandom();
                    Random r2 = helloRandom.getRandom();
                    System.out.println("Random 的两个实例是否指向同一个引用:" + (r1 == r2));
                    r1.printRandom();
                    r2.printRandom();
                  System.out.println();
                  System.out.println("下面两个实例采用方法注入");
                    Random r3 = helloRandom.createRandom();
                    Random r4 = helloRandom.createRandom();
                  System.out.println("Random 的两个实例是否指向同一个引用:" + (r3 == r4));
                  r3.printRandom();
                    r4.printRandom();
                 }catch(Exception e){
                     e.printStackTrace();
               }
          }
      }


      在src中建立一个bean.xml文件
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE beans PUBLIC "-//SPRING/DTD BEAN/EN"
                                        "http://www.springframework.org/dtd/spring-beans.dtd">
    <beans>
            <!-- 方法注入-->
              <bean id="myRandom" class="com.testspring.basic.lookup.Random" scope="prototype" />
              <bean id="helloRandom" class="com.testspring.basic.lookup.HelloAbstract">
                   <lookup-method name="createRandom" bean="myRandom"/>
                       <property name="random">
                               <ref local="myRandom" />  
                       </property>
             </bean>
     </beans>
     在根目录中建立一个build.xml文件
     <?xml version="1.0" encoding="UTF-8"?>
     <project name="MySpring" default="run" basedir=".">
                       <!-- 全局变量的定义 -->
           <property name="src.dir" value="src" />                       <!-- 源文件位置 -->
           <property name="classes.dir" value="classes" />     <!-- class文件位置 -->
           <property name="lib.dir" value="lib" />                          <!-- 引用的jar文件位置 -->
                      <!--当前工程环境变量-->
           <path id="Project-Classpath">
                  <pathelement path="${classes.dir}"/>
                <fileset dir="${lib.dir}">
                       <include name="*.jar"/>
                   </fileset>
           </path>
                      <!--编译-->
          <target name="compile" depends="clean">
                   <javac srcdir="${src.dir}" destdir="${classes.dir}">
                           <classpath refid="Project-Classpath" />
                   </javac>
          </target>
                      <!--复制除了java文件以外的文件-->
          <target name="copy-resources" depends="compile">
              <copy todir="${classes.dir}">
                    <fileset dir="${src.dir}">
                        <exclude name="**/*.java"/>
                    </fileset>
                   </copy>
        </target>
          <!--运行-->
        <target name="run">
                <java fork="true" classname="com.testspring.Test">
                        <classpath refid="Project-Classpath" />
                </java>
        </target>
     </project>
     将spring.jar,cglib-nodep-2.1_3.jar放入lib中。
     运行build命令就可以看到我们的结果了
    ----------------------------------------------------------------------------------------------
   下面两个实例没有采用方法注入
     Random 的两个实例是否指向同一个引用:true
     随机数是99
   随机数是99

    下面两个实例采用方法注入
    Random 的两个实例是否指向同一个引用:false
    随机数是98
    随机数是17

注意事项:

1.应用场合:一个singleton的Bean需要引用一个prototype的Bean; 一个无状态的Bean需要引用一个有状态的Bean; ... ; 等等情景下.
2.假定你有三个singleton,公用一个依赖,希望每个singleton有自己的实例依赖,所以将依赖设置成singleton,但
   实际上每个singleton在其生命周期中使用协作着的同一个实例足以,这种情况下,setter是最好的选择,使用方
   法查找注入会增加不必要的开销
3.我们也可以使用实现BeanFactoryAware的方式代替Lookup Method Injection

    public void setBeanFactory(Beanfactory factory){
         this.factory=factory;
   }
   public Random createRandom(){
      return (Random)factory.getBean("myRandom");
   }

   使用上述方法在效率上和使用本例的方法,相差无几(100000次差别在几毫秒),所以,建议使用本里的方法,减少和spring api的耦合

4.尽管可以不把查找方法定义成abstract,但还是建议这样做,可以防止不小心忘了配置查找方法而留一个空方法在代码中
5. 记住在任何时候只要定义了一个Bean的Lookup方法,那么这个Bean的实例将是一个CGLIB动态生成的实例而不是原来类的实例。
6.Spring允许在一个Bean中定义多个Lookup方法。
7.Spring还允许Lookup方法中定义的方法带有参数,但是Sping不会处理这些参数。
8.Spring对Lookup方法也存在一些限制:
方法不能是private的,但可以是protected的。
方法不能是静态的。

猜你喜欢

转载自ihyperwin.iteye.com/blog/1543476