下面通过一个示例来说明方法注入:
假设一个单例bean A每次在调用process()方法时,都需要一个新的bean B实例。具体实现如下:
//spring的xml配置如下
<bean id="b" class="com.bean.B" scope="prototype">
<bean id="a" class="com.bean.A"/>
/*
*ApplicationContextAware是spring提供的一个接口,
*通过实现它的setApplicationContext方法,可以拿到spring的上下文参数
*从而通过spring的上下文参数,获取spring容器管理的bean
*/
public class A implements ApplicationContextAware{
private ApplicationContext applicationContext;
public void setApplicationContext(
ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
public B createB() {
return this.applicationContext.getBean(B.class);
}
public void process(){
B b = createB();
b.execute();
}
}
public class B{
public void execute(){
System.out.println("execute");
}
}
通过以上代码我们可以看到,类A确实实现了在每次调用process()方法时,都有一个新的实例B为其服务。
但是也造成的类A的代码和Spring耦合在一起。那么怎样做才会更好呢,spring IoC给我们提供了一个更为高级的方式Method Injection,中文意思就是 “方法注入”。
关于方法注入的xml配置及注解配置,请看代码如下:
//spring的xml配置如下
<bean id="b" class="com.bean.B" scope="prototype">
<bean id="a" class="com.bean.A">
<lookup-method name="createB" bean="b"/>方法注入的配置
<bean>
public class A {
//@Lookup("b")注解的方式实现方法注入的配置
public B createB(){
return null;
}
public void process(){
B b = createB();
b.execute();
}
}
public class B{
public void execute(){
System.out.println("execute");
}
}
public class Test{
public static void main(String[] main){
ClassPathXmlApplicationContext ctxt =
new ClassPathXmlApplicationContext("bean.xml");
A a = ctxt.getBean(A.class);
System.out.println(a.createB()==a.createB());
System.out.println(a);
a.process();
}
}
//运行结果如下:
false
com.bean.A$$EnhancerBySpringCGLIB$$9f25fa1c@4671e53b
execute
上面代码的运行结果可能会让你出乎意料。这是因为Spring通过GCLIB动态代理生成字节码来实现对此方法的注入,,并且根据类B的配置属性scope="prototype"每次调用该方法都会生成一个新的实例B。
由于GCLIB是针对类来实现代理的,原理是对指定的类生成一个子类,并覆盖其中的方法实现代理。因为采用的是继承,所以不能对final修饰的类进行代理。 因此对于需要Spring容器子类化的类,不能使用final修饰,并且需要重写的方法也不能使用final修饰。
所以在进行方法注入时,需要注意注入的方法的标准格式如下:
<public|protected> [abstract] <return-type> theMethodName(no-arguments);