Spring原始碼分析(9)---FactoryBean(我是誰,誰是我,誰是誰)
阿新 • • 發佈:2019-02-18
本節,我們最分析ioc的最後一個核心點,那就是FactoryBean;
在ioc中,存在著這樣的一種bean,他的引用並不是只想他自身,而是通過折射指向了別的bean,就因為他的存在,使得他支援了jdbc,jndi等多種j2ee技術,他維持了spring的80%的功能的實現,那麼,就讓我們來詳細的分析一些這個神奇的bean,就好像武林外傳裡面的秀才殺死姬無命一樣的,誰到底是誰,我們看似在取得factoryBean,卻拿到了另外的一個類,失之東隅,收之桑榆;
我們以MethodInvokingFactoryBean為例子,這是一個很奇妙的bean,如下配置:
我們具體來看下幾個類的程式碼:
InitializingBean介面的話,我們已經不用介紹了;
MethodInvoker類主要含有這幾個類:
MethodInvoker類正式實現了MethodInvokingFactoryBean方法轉嫁的核心功能,我們從入口來分析一下這個類:
在這裡分別呼叫了開始準備的目標類和目標方法;得到結果並且把他儲存在singletonObject中,
在factoryBean介面中,實現了這個方法:
我們來看一下getBean的實現:
在AbstractBeanFactory中,
public Object getBean(String name, Class requiredType, Object[] args) throws BeansException
是一個典型的模板模式的應用,他的createBean();在AbstractAutowireCapableBeanFactory實現,他負責從BeanDefinition構造出一個Objct,而getBean在拿到這個Object後,就會呼叫第三步:
bean = getObjectForSharedInstance(name, sharedInstance);
從這個Object類中拿到一個真正我們要返回出去的bean,而這裡主要就是我們的factoryBean的處理;
isFactoryDereference(name)判斷一個類名是不是以&開頭,如果這個類是FactoryBean並且beanName以&開頭,那麼則返回這個FactoryBean的本身,如果不是以&開始,則返回他所要轉換的類;
beanInstance = factory.getObject();
就這樣,我們在不知不覺的就完成這次南水北調的轉換! *_*
最後,我們來總結一下Spring程式碼的此功能實現,在整個實現中,spring的程式碼都很有調理,很好的體現了面向介面程式設計,幾乎每個具體的類(工具類除外)都是從介面開始著手,並且一層一層就想洋蔥一樣展現在我們的眼前,比如,首先,抽象出來一個頂層的factoryBean介面,提供了bean轉換的統一介面,為我們組合型模板方式提供了可能,我們只要在beanFactory中呼叫他的getObject,而不必管這個類是從jndi還是從別的類的方法中得到的,從而把BeanFactory與具體的FactoryBean實現解耦開來,而且在MethodInvokingFacrotyBean中,我們把轉換方法的實現用繼承的方式委託給了MethodInvoker,功能複用的方式有兩種,一種如這個一樣的繼承,壞處就是這是一種編譯器的複用,無法實現策略模式一樣的轉換演算法的功能,但是他好的就是我們不用顯示的把委託程式碼重新寫一次,而組合複用的好處是我們可以更換不同的MethodInvoker的實現,如果這裡的targetClass和targetMethod有另外的處理方式,即MethodInvoker有另外的實現方式,我們可以考慮使用,如果沒有的話,我們選擇更方便的繼承方式;
在程式碼中,大量的使用了工具類,這是單一職責原則的體現,為了避免意外的情況發生,或者說使用者的盲目擴充套件,很多公開地方做了防禦性的判斷,並且在意外情況發生,輸入引數非法的地方,丟擲了明確的異常,這正式程式碼大全裡所說的防禦性程式設計的良好的體現,往往是這麼一些微小的細節,更加的體現了大牛們程式碼的質量;設計的合理;這些都是值得我們深入去研究的地方;
因為我也初出茅廬,這些文章也只是我看spring原始碼的一些小小的心得,在分享給有需要的朋友的同時,其實更多的是給自己招一個隨處可得的筆記本,希望大家能夠多多交流,在前面的getBean方法中也許分析得有些模糊,但是通過前幾節對反射的重新複習,cglib的應用和asm的瞭解,和對ioc的程式碼一些全域性的瞭解,現在我們再去看getBean程式碼,基本上不存在著什麼疑問,至少我是這樣的;所以,我想ioc的分析,應該就差不多到這裡了,而spring的微核就是這個ioc容器,搞清楚這些對我們將來分析外圍打下一個良好的基礎;
在ioc中,存在著這樣的一種bean,他的引用並不是只想他自身,而是通過折射指向了別的bean,就因為他的存在,使得他支援了jdbc,jndi等多種j2ee技術,他維持了spring的80%的功能的實現,那麼,就讓我們來詳細的分析一些這個神奇的bean,就好像武林外傳裡面的秀才殺死姬無命一樣的,誰到底是誰,我們看似在取得factoryBean,卻拿到了另外的一個類,失之東隅,收之桑榆;
我們以MethodInvokingFactoryBean為例子,這是一個很奇妙的bean,如下配置:
- <beanname="methodInvoke"class="org.springframework.beans.factory.config.MethodInvokingFactoryBean"
- <propertyname="staticMethod">
- <value>org.corey.demo.Demo.staticMethod</value>
- </property>
- </bean>
- ApplicationContext ac=new ClassPathXmlApplicationContext("applicationContext.xml");
- ac.getBean("methodInvoke");
- ac.getBean("methodInvoke");返回的是org.corey.demo.Demo類的staticMethod方法的返回值;
我們具體來看下幾個類的程式碼:
- publicinterface FactoryBean {
- Object getObject() throws Exception;
- Class getObjectType();
- boolean isSingleton();
- }
InitializingBean介面的話,我們已經不用介紹了;
MethodInvoker類主要含有這幾個類:
- private Class targetClass;
- private Object targetObject;
- private String targetMethod;
- private Object[] arguments;
- // the method we will call
- private Method methodObject;
MethodInvoker類正式實現了MethodInvokingFactoryBean方法轉嫁的核心功能,我們從入口來分析一下這個類:
- publicvoid afterPropertiesSet() throws Exception {
- prepare();
- if (this.singleton) {
- Object obj = doInvoke();
- this.singletonObject = (obj != null ? obj : MethodInvoker.VOID);
- }
- }
- ublic void prepare() throws ClassNotFoundException, NoSuchMethodException {
- if (this.targetClass == null) {
- thrownew IllegalArgumentException("Either targetClass or targetObject is required");
- }
- if (this.targetMethod == null) {
- thrownew IllegalArgumentException("targetMethod is required");
- }
- if (this.arguments == null) {
- this.arguments = new Object[0];
- }
- Class[] argTypes = new Class[this.arguments.length];
- for (int i = 0; i < this.arguments.length; ++i) {
- argTypes[i] = (this.arguments[i] != null ? this.arguments[i].getClass() : Object.class);
- }
- // Try to get the exact method first.
- try {
- this.methodObject = this.targetClass.getMethod(this.targetMethod, argTypes);
- }
- catch (NoSuchMethodException ex) {
- // Just rethrow exception if we can't get any match.
- this.methodObject = findMatchingMethod();
- if (this.methodObject == null) {
- throw ex;
- }
- }
- if (this.targetObject == null && !Modifier.isStatic(this.methodObject.getModifiers())) {
- thrownew IllegalArgumentException("Target method must not be non-static without a target");
- }
- }
- private Object doInvoke() throws Exception {
- try {
- return invoke();
- }
- catch (InvocationTargetException ex) {
- if (ex.getTargetException() instanceof Exception) {
- throw (Exception) ex.getTargetException();
- }
- if (ex.getTargetException() instanceof Error) {
- throw (Error) ex.getTargetException();
- }
- throw ex;
- }
- }
- public Object invoke() throws InvocationTargetException, IllegalAccessException {
- if (this.methodObject == null) {
- thrownew IllegalStateException("prepare() must be called prior to invoke() on MethodInvoker");
- }
- // In the static case, target will just be <code>null</code>.
- returnthis.methodObject.invoke(this.targetObject, this.arguments);
- }
在這裡分別呼叫了開始準備的目標類和目標方法;得到結果並且把他儲存在singletonObject中,
在factoryBean介面中,實現了這個方法:
- public Object getObject() throws Exception {
- if (this.singleton) {
- // Singleton: return shared object.
- returnthis.singletonObject;
- }
- else {
- // Prototype: new object on each call.
- Object retVal = doInvoke();
- return (retVal != null ? retVal : MethodInvoker.VOID);
- }
- }
我們來看一下getBean的實現:
在AbstractBeanFactory中,
public Object getBean(String name, Class requiredType, Object[] args) throws BeansException
是一個典型的模板模式的應用,他的createBean();在AbstractAutowireCapableBeanFactory實現,他負責從BeanDefinition構造出一個Objct,而getBean在拿到這個Object後,就會呼叫第三步:
bean = getObjectForSharedInstance(name, sharedInstance);
從這個Object類中拿到一個真正我們要返回出去的bean,而這裡主要就是我們的factoryBean的處理;
- protected Object getObjectForSharedInstance(String name, Object beanInstance) throws BeansException {
- String beanName = transformedBeanName(name);
- // Don't let calling code try to dereference the
- // bean factory if the bean isn't a factory.
- if (isFactoryDereference(name) && !(beanInstance instanceof FactoryBean)) {
- thrownew BeanIsNotAFactoryException(beanName, beanInstance.getClass());
- }
- // Now we have the bean instance, which may be a normal bean or a FactoryBean.
- // If it's a FactoryBean, we use it to create a bean instance, unless the
- // caller actually wants a reference to the factory.
- if (beanInstance instanceof FactoryBean) {
- if (!isFactoryDereference(name)) {
- // Return bean instance from factory.
- FactoryBean factory = (FactoryBean) beanInstance;
- if (logger.isDebugEnabled()) {
- logger.debug("Bean with name '" + beanName + "' is a factory bean");
- }
- try {
- beanInstance = factory.getObject();
- }
- catch (Exception ex) {
- thrownew BeanCreationException(beanName, "FactoryBean threw exception on object creation", ex);
- }
- if (beanInstance == null) {
- thrownew FactoryBeanNotInitializedException(
- beanName, "FactoryBean returned null object: " +
- "probably not fully initialized (maybe due to circular bean reference)");
- }
- }
- else {
- // The user wants the factory itself.
- if (logger.isDebugEnabled()) {
- logger.debug("Calling code asked for FactoryBean instance for name '" + beanName + "'");
- }
- }
- }
- return beanInstance;
- }
isFactoryDereference(name)判斷一個類名是不是以&開頭,如果這個類是FactoryBean並且beanName以&開頭,那麼則返回這個FactoryBean的本身,如果不是以&開始,則返回他所要轉換的類;
beanInstance = factory.getObject();
就這樣,我們在不知不覺的就完成這次南水北調的轉換! *_*
最後,我們來總結一下Spring程式碼的此功能實現,在整個實現中,spring的程式碼都很有調理,很好的體現了面向介面程式設計,幾乎每個具體的類(工具類除外)都是從介面開始著手,並且一層一層就想洋蔥一樣展現在我們的眼前,比如,首先,抽象出來一個頂層的factoryBean介面,提供了bean轉換的統一介面,為我們組合型模板方式提供了可能,我們只要在beanFactory中呼叫他的getObject,而不必管這個類是從jndi還是從別的類的方法中得到的,從而把BeanFactory與具體的FactoryBean實現解耦開來,而且在MethodInvokingFacrotyBean中,我們把轉換方法的實現用繼承的方式委託給了MethodInvoker,功能複用的方式有兩種,一種如這個一樣的繼承,壞處就是這是一種編譯器的複用,無法實現策略模式一樣的轉換演算法的功能,但是他好的就是我們不用顯示的把委託程式碼重新寫一次,而組合複用的好處是我們可以更換不同的MethodInvoker的實現,如果這裡的targetClass和targetMethod有另外的處理方式,即MethodInvoker有另外的實現方式,我們可以考慮使用,如果沒有的話,我們選擇更方便的繼承方式;
在程式碼中,大量的使用了工具類,這是單一職責原則的體現,為了避免意外的情況發生,或者說使用者的盲目擴充套件,很多公開地方做了防禦性的判斷,並且在意外情況發生,輸入引數非法的地方,丟擲了明確的異常,這正式程式碼大全裡所說的防禦性程式設計的良好的體現,往往是這麼一些微小的細節,更加的體現了大牛們程式碼的質量;設計的合理;這些都是值得我們深入去研究的地方;
因為我也初出茅廬,這些文章也只是我看spring原始碼的一些小小的心得,在分享給有需要的朋友的同時,其實更多的是給自己招一個隨處可得的筆記本,希望大家能夠多多交流,在前面的getBean方法中也許分析得有些模糊,但是通過前幾節對反射的重新複習,cglib的應用和asm的瞭解,和對ioc的程式碼一些全域性的瞭解,現在我們再去看getBean程式碼,基本上不存在著什麼疑問,至少我是這樣的;所以,我想ioc的分析,應該就差不多到這裡了,而spring的微核就是這個ioc容器,搞清楚這些對我們將來分析外圍打下一個良好的基礎;