1.Lookup方法注入
在大部分的應用場景中,容器中的大部分bean是singleton類型的。當一個單例bean需要和另外一個單例bean協作時,或者一個非單例bean要引用另外一個非單例bean時,通常情況下将一個bean定義為另外一個bean的屬性值就行了。不過對于具有不同生命周期的bean來說這樣做就會有問題了,比如在調用一個單例類型bean A的某個方法,需要引用另一個非單例(prototype)類型bean B,對于bean A來說,容器隻會建立一次,這樣就沒法在需要的時候每次讓容器為bean A提供一個新的bean
B執行個體。
Lookup方法具有使容器覆寫受容器管理的bean方法的能力,進而傳回指定名字的bean執行個體。在上述場景中,Lookup方法注入适用于原型bean。 Lookup方法注入的内部機制是Spring利用了CGLIB庫在運作時生成二進制代碼的功能,通過動态建立Lookup方法bean的子類進而達到複寫Lookup方法的目的。
為了使動态子類起作用,Spring容器要子類化的類不能是
final
,并且需要覆寫的方法也不能是 final
。同樣的,要測試一個包含 抽象
方法的類也稍微有些不同,你需要子集編寫它的子類提供該 抽象
方法的實作。最後,作為方法注入目标的bean不能是序列化的。在Spring
3.2之後再也沒必要添加CGLIB到classpath,因為CGLIB的類打包在了org.springframework下并且在Spring核心JAR中有所描述。這樣做既友善,又避免了與其他使用了不同版本CGLIB的項目的沖突。
假如現在有2個類:Clerk和ClerkManager,其中ClerkManager依賴于Clerk,即ClerkManager持有類型為Clerk的私有屬性,現在我們想讓Clerk been的作用域為singleton,而ClerkManager been的作用域為prototype,如果不做任何處理直接在ClerkManager been中設定Clerk屬性,就會出現問題,即每次應用的Clerk been都是同一個,無法達到預期的效果。這個時候我們可以使用Spring的Lookup方法注入來達到在每次引用Clerk屬性的時候都會動态建立一個新的Clerk
been的目的。一下是Clerk和ClerkManager的代碼:
package com.ioc.lookup;
public class Clerk {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
package com.ioc.lookup;
public abstract class ClerkManager {
private Clerk clerk;
public Object process() {
//調用createClerk()方法動态生成Clerk對象
Clerk Clerk=createClerk();
return Clerk;
}
//這個動态生成Clerk對象的方法,這是個抽象方法,Spring容器會自動覆寫createClerk()方法的實作。
protected abstract Clerk createClerk();
public Clerk getClerk() {
return clerk;
}
public void setClerk(Clerk clerk) {
this.clerk = clerk;
}
}
接着我們在applicationContext.xml中配置如上類的bean:
<bean id="clerk" class="com.ioc.lookup.Clerk" scope="prototype">
<property name="name" value="Tom"></property>
<property name="age" value="20"></property>
</bean>
<bean id="clerkManager" class="com.ioc.lookup.ClerkManager" scope="singleton">
<lookup-method name="createClerk" bean="clerk"/>
</bean>
最後編寫一個測試類:
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.ioc.lookup.ClerkManager;
public class LookuoDemo {
public static void main(String[] args) {
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
ClerkManager clerkManager=(ClerkManager) applicationContext.getBean("clerkManager");
System.out.println("第一次注入的Clerk:"+clerkManager.process());
System.out.println("第二次注入的Clerk:"+clerkManager.process());
}
}
運作測試類,結果如下:
可以看到,兩次注入的Clerk been是不一樣的,是以達到了我們的目的。