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的。
方法不能是静态的。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值