Spring內核研究-Lookup方法注入
Lookup方法注入
“Lookup方法”可以使Spring替換一個bean原有的,獲取其它對象具體的方法,并自動返回在容器中的查找結果。
我們來看這個例子:
UserDao.java
在UserDao的構造函數中接受一個name參數,創建UserDao的對象會把自己的名字傳遞給userDao,這樣userDao的create方法中就會把userDao的創建者打印出來。
package research.spring.beanfactory.ch2; public class UserDao { private String name=""; public UserDao(String name){ this.name=name; } public void create(){ System.out.println("create user from - "+name); } }
UserManager.java
在這段代碼中UserManager依靠getUserDao方法來獲取UserDao對象。由于在getUserDao方法里顯示的聲明了如何去實例一個UserDao,所以上面的代碼不符合IoC模式的風格。雖然使用GetUserDao封裝了UserDao的創建過程,但是UserManager和UserDao的關系仍然非常緊密。
package research.spring.beanfactory.ch2; public class UserManager { public UserDao getUserDao() { return new UserDao("UserManager.getUserDao()"); } public void createUser() { UserDao dao = getUserDao(); //通過getUserDao獲得userDao dao.create(); } }
LookupMethodTest.java
通過BeanFactory獲得UserManager,并調用createUser方法。
context.xmlpackage research.spring.beanfactory.ch2; import org.springframework.beans.factory.xml.XmlBeanFactory; import org.springframework.core.io.ClassPathResource; public class LookupMethodTest { public static void main(String[] args) { XmlBeanFactory factory=new XmlBeanFactory(new ClassPathResource("research/spring/beanfactory/ch2/context.xml")); UserManager manager=(UserManager) factory.getBean("userManager"); manager.createUser(); //create a User } }
xml version="1.0" encoding="UTF-8"?> DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"> <beans> <bean name="userManager" class="research.spring.beanfactory.ch2.UserManager"> bean> <bean name="userDao class="research.spring.beanfactory.ch2.UserDao" > bean> beans>
運行LookupMethodTest你會看到屏幕輸入” create user from - UserManager.getUserDao()”。
由于是遺留系統,所以我們不能修改UserManager。現在我希望讓這個UserManager依賴的Dao對象由spring管理,而不修改原有的代碼。
在這個場景中我們就可以利用Spring提供的“Lookup方法”來替換原有的getUserDao方法,實現自動獲取userDao的功能。修改context.xml:
在這個場景中我們就可以利用Spring提供的“Lookup方法”來替換原有的getUserDao方法,實現自動獲取userDao的功能。修改context.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 name="userManager" class="research.spring.beanfactory.ch2.UserManager"> <lookup-method name="getUserDao" bean="userDao" /> bean> <bean name="userDao" class="research.spring.beanfactory.ch2.UserDao" > <constructor-arg> <value>lookup methodvalue> constructor-arg> bean> <bean name="userDaoFactory" class="research.spring.beanfactory.ch2.UserDaoFactory"> bean> beans>
再次運行LookupMethodTest你會看到不同的輸出結果“create user from - lookup method”。字符串“lookup method”是通過構造函數注入給userDao的。原來的userManager.java并沒有作任何修改,仍然是通過UserDao dao = getUserDao();來獲得userDao的。這說明Spring已經替換了原有的getUserDao方法的實現,當執行getUserDao時Spring會在容器中尋找指定的Bean,并返回這個Bean。
Lookup方法的工作機制
通過這種機制我們可以在不修改原系統代碼的情況下,可以輕易的把UserDao換成別的類型相容的對象而不會影響原系統。Spring是使用CGLIB在字節碼級別動態實現出userManager的子類,并重寫getUserDao方法的方式來實現這個神奇的功能的。
修改LookupMethodTest.java:
package research.spring.beanfactory.ch2; import org.springframework.beans.factory.xml.XmlBeanFactory; import org.springframework.core.io.ClassPathResource; public class LookupMethodTest { public static void main(String[] args) { XmlBeanFactory factory=new XmlBeanFactory(new ClassPathResource(
"research/spring/beanfactory/ch2/context.xml")); UserManager manager=(UserManager) factory.getBean("userManager"); System.out.println(manager.toString()); //打印userManager的信息 manager.createUser(); //create a User } }
我們在獲取UserManager的實例后打印出這個實例的信息,再次運行LookupMethodTest你會看到:
注意manager.toString()打印出了:
這個是CGLIG動態生成的類,而不是原來的UserManager的實例。所以請記住在任何時候只要定義了一個Bean的Lookup方法,那么這個Bean的實例將是一個CGLIB動態生成的實例而不是原來類的實例。
Spring允許在一個Bean中定義多個Lookup方法。
<bean name="userManager" class="research.spring.beanfactory.ch2.UserManager"> <lookup-method name="getUserDao" bean="userDao" /> <lookup-method name="getOtherDao" bean="otherDao" /> bean>
上面的做法是合法的,并且可以正常工作。
雖然Spring會用CGLIB動態生成一個帶有Lookup方法bean的子類,但是這并不影響Spring完成其它的功能。Sping還是允許Lookup方法和setXXX、構造函數注入以及后面我們將介紹的自動依賴檢查和自動裝配的功能同時使用。
Spring還允許Lookup方法中定義的方法帶有參數,但是Sping不會處理這些參數。
修改UserManager:
修改UserManager:
package research.spring.beanfactory.ch2; public class UserManager { private UserDao dao; public void setDao(UserDao dao) { this.dao = dao; } public UserDao getUserDao(String daoName) { return new UserDao("UserManager.getUserDao()"); } public void createUser() { UserDao dao = getUserDao(“userDao”); //通過getUserDao獲得userDao dao.create(); } }
雖然方法上由參數,但是上面的代碼可以正常工作。Spring不會處理這些參數。
Spring對Lookup方法也存在一些限制:
- 方法不能是private的,但可以是protected的。
- 方法不能是靜態的。
在抽象類和接口上應用Lookup方法
有一個比較有趣的用法,就是在抽象類上定義Lookup方法。你一定記得經典的工廠模式吧。定義一個抽象工廠,然后為每一類具體產品實現一個具體產品的工廠。
一個抽象工廠:
package research.spring.beanfactory.ch2; public abstract class Factory { public abstract UserDao getProduct(); }
具體一類產品的工廠:
package research.spring.beanfactory.ch2; public class UserDaoFactory extends Factory{ public UserDao getProduct(){ return new UserDao("UserDaoFactory"); } }
用戶可以通過:
new UserDaoFactory().getProduce();
來獲取具體的UserDao產品。
但是如果有很多產品就需要做出實現出很多工廠如,DocumentDaoFactory、GroupDaoFactory等等,這樣系統中會出現大量的工廠。工廠的泛濫并不能說明系統的設計是合理的。
既然Spring可以在抽象類上使用Lookup方法,那么我們就可以不同實現真的去實現那么多的子類了。我們可以在抽象類上直接定義Lookup方法和目標對象。用戶直接通過抽象類來獲得需要的產品對象。看下面這個例子:
Factory.ava
package research.spring.beanfactory.ch2; public abstract class Factory { public abstract Object getProduct(); }
context.xml
如果指定userDaoFactory的類為一個抽象類,并且再這個bean里定義了Lookup方法,那么Spring會自動生成這個抽象類的子類實現。
xml version="1.0" encoding="UTF-8"?> DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"> <beans> <bean name="userManager" class="research.spring.beanfactory.ch2.UserManager"> <lookup-method name="getUserDao" bean="userDao" /> bean> <bean name="userDao" class="research.spring.beanfactory.ch2.UserDao" > <constructor-arg> <value>lookup methodvalue> constructor-arg> bean> <bean name="userDaoFactory" class="research.spring.beanfactory.ch2.Factory" singleton="false"> <lookup-method name="getProduct" bean="userDao" /> bean> beans>
Test.java
package research.spring.beanfactory.ch2; import org.springframework.beans.factory.xml.XmlBeanFactory; import org.springframework.core.io.ClassPathResource; public class LookupMethodTest { public static void main(String[] args) { XmlBeanFactory factory=new XmlBeanFactory(new ClassPathResource(
"research/spring/beanfactory/ch2/context.xml")); //獲得抽象工廠 Factory abstractFactory=(Factory) factory.getBean("userDaoFactory"); UserDao userDao=(UserDao) abstractFactory.getProduct(); System.out.println(userDao.toString()); userDao.create(); } }
運行Test你會看到:
research.spring.beanfactory.ch2.UserManager$$EnhancerByCGLIB$$cc2f8f1c@12c7568
create user from - lookup method
對,這個結果和上面的例子是完全一樣的。UserDao并沒有改變,我們通過抽象的Factory獲得了具體的UserDao的實例。這樣即使系統中很多的具體產品我們也不需要實現每類產品的工廠類了。只需要在系統中配置多個抽象工廠,并且配置每個工廠的singlton為false,在用戶使用時使用不同抽象工廠的實例就可以了。
<bean name="userDaoFactory" class="research.spring.beanfactory.ch2.Factory" singleton="false"> <lookup-method name="getProduct" bean="userDao" /> bean> <bean name="documentDaoFactory" class="research.spring.beanfactory.ch2.Factory" singleton="false"> <lookup-method name="getProduct" bean="documentDao" /> bean>
Spring不關心抽象類中的定義的lookup方法是否時抽象的,Spring都會重寫這個方法。
既然Sping可以動態實現抽象類的子類那么,它能不能動態創建出實現一個接口的類呢。答案時肯定的。上面的例子可以直接把Factory變成一個接口,仍然可以正常工作。
這里需要注意的是,只要在一個Bean上明確的定義了Lookup方法,Spring才會使用CGLIB來做原對象的字節碼代理。如果一個沒有定義Lookup方法的抽象類或接口是不能直接被Spring實例的。
posted on 2007-04-09 02:57 crazy zerlot 閱讀(727) 評論(0) 編輯 收藏