1. 程式人生 > >Spring原始碼分析(9)---FactoryBean(我是誰,誰是我,誰是誰)

Spring原始碼分析(9)---FactoryBean(我是誰,誰是我,誰是誰)

本節,我們最分析ioc的最後一個核心點,那就是FactoryBean;
在ioc中,存在著這樣的一種bean,他的引用並不是只想他自身,而是通過折射指向了別的bean,就因為他的存在,使得他支援了jdbc,jndi等多種j2ee技術,他維持了spring的80%的功能的實現,那麼,就讓我們來詳細的分析一些這個神奇的bean,就好像武林外傳裡面的秀才殺死姬無命一樣的,誰到底是誰,我們看似在取得factoryBean,卻拿到了另外的一個類,失之東隅,收之桑榆;

我們以MethodInvokingFactoryBean為例子,這是一個很奇妙的bean,如下配置:

  1. <beanname="methodInvoke"class="org.springframework.beans.factory.config.MethodInvokingFactoryBean"
    >
  2. <propertyname="staticMethod">
  3. <value>org.corey.demo.Demo.staticMethod</value>
  4. </property>
  5. </bean>
我們在程式碼:
  1. ApplicationContext ac=new ClassPathXmlApplicationContext("applicationContext.xml");
  2. ac.getBean("methodInvoke");

  1. ac.getBean("methodInvoke");返回的是org.corey.demo.Demo類的staticMethod方法的返回值;
那麼,我們來看一下這些都是怎麼實現的:



我們具體來看下幾個類的程式碼:

  1. publicinterface FactoryBean {
  2.     Object getObject() throws Exception;
  3.     Class getObjectType();
  4. boolean isSingleton();
  5. }

InitializingBean介面的話,我們已經不用介紹了;

MethodInvoker類主要含有這幾個類:

  1. private Class targetClass;
  2. private Object targetObject;
  3. private String targetMethod;
  4. private Object[] arguments;
  5. // the method we will call
  6. private Method methodObject;

MethodInvoker類正式實現了MethodInvokingFactoryBean方法轉嫁的核心功能,我們從入口來分析一下這個類:

  1. publicvoid afterPropertiesSet() throws Exception {
  2.         prepare();
  3. if (this.singleton) {
  4.             Object obj = doInvoke();
  5. this.singletonObject = (obj != null ? obj : MethodInvoker.VOID);
  6.         }
  7.     }
這是MethodInvokingFactoryBean的初始化方法:
  1. ublic void prepare() throws ClassNotFoundException, NoSuchMethodException {
  2. if (this.targetClass == null) {
  3. thrownew IllegalArgumentException("Either targetClass or targetObject is required");
  4.         }
  5. if (this.targetMethod == null) {
  6. thrownew IllegalArgumentException("targetMethod is required");
  7.         }
  8. if (this.arguments == null) {
  9. this.arguments = new Object[0];
  10.         }
  11.         Class[] argTypes = new Class[this.arguments.length];
  12. for (int i = 0; i < this.arguments.length; ++i) {
  13.             argTypes[i] = (this.arguments[i] != null ? this.arguments[i].getClass() : Object.class);
  14.         }
  15. // Try to get the exact method first.
  16. try {
  17. this.methodObject = this.targetClass.getMethod(this.targetMethod, argTypes);
  18.         }
  19. catch (NoSuchMethodException ex) {
  20. // Just rethrow exception if we can't get any match.
  21. this.methodObject = findMatchingMethod();
  22. if (this.methodObject == null) {
  23. throw ex;
  24.             }
  25.         }
  26. if (this.targetObject == null && !Modifier.isStatic(this.methodObject.getModifiers())) {
  27. thrownew IllegalArgumentException("Target method must not be non-static without a target");
  28.         }
  29.     }
將你在xml檔案中配置的MethodInvokingFactoryBean的屬性(字串)轉化成為元資料,為等下的呼叫做準備;

  1. private Object doInvoke() throws Exception {
  2. try {
  3. return invoke();
  4.         }
  5. catch (InvocationTargetException ex) {
  6. if (ex.getTargetException() instanceof Exception) {
  7. throw (Exception) ex.getTargetException();
  8.             }
  9. if (ex.getTargetException() instanceof Error) {
  10. throw (Error) ex.getTargetException();
  11.             }
  12. throw ex;
  13.         }
  14.     }
  1. public Object invoke() throws InvocationTargetException, IllegalAccessException {
  2. if (this.methodObject == null) {
  3. thrownew IllegalStateException("prepare() must be called prior to invoke() on MethodInvoker");
  4.         }
  5. // In the static case, target will just be <code>null</code>.
  6. returnthis.methodObject.invoke(this.targetObject, this.arguments);
  7.     }

在這裡分別呼叫了開始準備的目標類和目標方法;得到結果並且把他儲存在singletonObject中,

在factoryBean介面中,實現了這個方法:

  1. public Object getObject() throws Exception {
  2. if (this.singleton) {
  3. // Singleton: return shared object.
  4. returnthis.singletonObject;
  5.         }
  6. else {
  7. // Prototype: new object on each call.
  8.             Object retVal = doInvoke();
  9. return (retVal != null ? retVal : MethodInvoker.VOID);
  10.         }
  11.     }
用getObject把真正的bean返回出去了,那麼,現在我們的疑問來了,我們在getBean的時候,什麼時候呼叫了這個getObject()方法呢???

我們來看一下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的處理;
  1. protected Object getObjectForSharedInstance(String name, Object beanInstance) throws BeansException {
  2.         String beanName = transformedBeanName(name);
  3. // Don't let calling code try to dereference the
  4. // bean factory if the bean isn't a factory.
  5. if (isFactoryDereference(name) && !(beanInstance instanceof FactoryBean)) {
  6. thrownew BeanIsNotAFactoryException(beanName, beanInstance.getClass());
  7.         }
  8. // Now we have the bean instance, which may be a normal bean or a FactoryBean.
  9. // If it's a FactoryBean, we use it to create a bean instance, unless the
  10. // caller actually wants a reference to the factory.
  11. if (beanInstance instanceof FactoryBean) {
  12. if (!isFactoryDereference(name)) {
  13. // Return bean instance from factory.
  14.                 FactoryBean factory = (FactoryBean) beanInstance;
  15. if (logger.isDebugEnabled()) {
  16.                     logger.debug("Bean with name '" + beanName + "' is a factory bean");
  17.                 }
  18. try {
  19.                     beanInstance = factory.getObject();
  20.                 }
  21. catch (Exception ex) {
  22. thrownew BeanCreationException(beanName, "FactoryBean threw exception on object creation", ex);
  23.                 }
  24. if (beanInstance == null) {
  25. thrownew FactoryBeanNotInitializedException(
  26.                         beanName, "FactoryBean returned null object: " +
  27. "probably not fully initialized (maybe due to circular bean reference)");
  28.                 }
  29.             }
  30. else {
  31. // The user wants the factory itself.
  32. if (logger.isDebugEnabled()) {
  33.                     logger.debug("Calling code asked for FactoryBean instance for name '" + beanName + "'");
  34.                 }
  35.             }
  36.         }
  37. return beanInstance;
  38.     }
transformedBeanName去掉了beanName的&符號;
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容器,搞清楚這些對我們將來分析外圍打下一個良好的基礎;