莊周夢蝶

          生活、程序、未來
             :: 首頁 ::  ::  :: 聚合  :: 管理

          spring IOC容器實現探討

          Posted on 2007-04-20 11:59 dennis 閱讀(15533) 評論(12)  編輯  收藏 所屬分類: 源碼解讀
              spring IOC容器的實現,一開始我被復雜的接口和類所掩埋,看不清整體的思路和設計,踟躕于代碼叢林中,摸不清前進的方向。一開始我就決定只研讀以xml文件做配置文件的XmlFactoryBean的具體實現為主要目標,漸漸地有了點感覺,用UML把spring中的bean工廠體系展現出來之后就更清晰了,讓你不得不感嘆設計的精巧和復雜。本文只是我個人對spring IOC實現的理解,如有錯誤,請不吝賜教,謝謝。
              首先,需要理解的是spring容器中bean的生命周期,《spring in action》中的那張圖是最好的解釋,結合這張圖和源碼來解讀spring中IOC的實現將非常容易理解。這張圖完整展示了spring容器中一個bean從創建到銷毀的整個生命周期。
             
          1. 容器尋找Bean的定義信息并且將其實例化。
          2.受用依賴注入,Spring按照Bean定義信息配置Bean的所有屬性。
          3.如果Bean實現了BeanNameAware接口,工廠調用Bean的setBeanName()方法傳遞Bean的ID。
          4.如果Bean實現了BeanFactoryAware接口,工廠調用setBeanFactory()方法傳入工廠自身。
          5.如果BeanPostProcessor和Bean關聯,那么它們的postProcessBeforeInitialzation()方法將被調用。
          6.如果Bean指定了init-method方法,它將被調用。
          7.最后,如果有BeanPsotProcessor和Bean關聯,那么它們的postProcessAfterInitialization()方法將被調用。
              到這個時候,Bean已經可以被應用系統使用了,并且將被保留在Bean Factory中知道它不再需要。有兩種方法可以把它從Bean Factory中刪除掉。
          1.如果Bean實現了DisposableBean接口,destory()方法被調用。
          2.如果指定了訂制的銷毀方法,就調用這個方法。
             
              下面我們將會看到,這些bean創建銷毀的每個階段是如何在源碼中實現的。
              再看看spring中bean工廠的完整體系,比較復雜,不過我們只需要關注其中的幾個核心工廠。
              
              (看不清楚,請下載圖片來看,比較清晰)
              這些工廠類沒有在同一個包內,分布在org.springframework.beans以及它的子包內,把它們放在一起就看的比較清楚了。我們需要關注的是這么兩個類:org.springframework.beans.factory.support.AbstractBeanFactory
          org.springframework.beans.factory.support.DefaultListableBeanFactory
          以及接口:
          org.springframework.beans.factory.support.BeanDefinitionRegistry

              AbstractBeanFactory作為BeanFactory接口的抽象實現類,是其他工廠類的父類,提供了bean的singlton緩存、singleton/prototype的決定、bean的別名以及bean和它的子類bean的定義合并(bean definition merging for child bean definitions)和銷毀。這里有3個重載的getBean方法(實現BeanFactory定義的getBean方法),我們關注下最主要的這個方法:
          public Object getBean(String name, Class requiredType, Object[] args) throws BeansException {
                  String beanName 
          = transformedBeanName(name);
                  Object bean 
          = null;

                  
          // Eagerly check singleton cache for manually registered singletons.
                  Object sharedInstance = null;
                  
          //從單例緩存中獲取
                  synchronized (this.singletonCache) {
                      sharedInstance 
          = this.singletonCache.get(beanName);
                  }
                  
          if (sharedInstance != null) {
                      
          if (isSingletonCurrentlyInCreation(beanName)) {
                          
          if (logger.isDebugEnabled()) {
                              logger.debug(
          "Returning eagerly cached instance of singleton bean '" + beanName +
                                      
          "' that is not fully initialized yet - a consequence of a circular reference");
                          }
                      }
                      
          else {
                          
          if (logger.isDebugEnabled()) {
                              logger.debug(
          "Returning cached instance of singleton bean '" + beanName + "'");
                          }
                      }
                      bean 
          = getObjectForSharedInstance(name, sharedInstance);
                  }

                  
          else {
                      
          // Fail if we're already creating this singleton instance:
                      
          // We're assumably within a circular reference.
                      if (isSingletonCurrentlyInCreation(beanName)) {
                          
          throw new BeanCurrentlyInCreationException(beanName);
                      }

                      
          // Check if bean definition exists in this factory.
                      
          //檢測bean是否定義在父工廠
                      if (getParentBeanFactory() != null && !containsBeanDefinition(beanName)) {
                          
          // Not found -> check parent.
                          if (getParentBeanFactory() instanceof AbstractBeanFactory) {
                              
          // Delegation to parent with args only possible for AbstractBeanFactory.
                              return ((AbstractBeanFactory) getParentBeanFactory()).getBean(name, requiredType, args);
                          }
                          
          else if (args == null) {
                              
          // No args -> delegate to standard getBean method.
                              return getParentBeanFactory().getBean(name, requiredType);
                          }
                          
          else {
                              
          throw new NoSuchBeanDefinitionException(beanName,
                                      
          "Cannot delegate to parent BeanFactory because it does not supported passed-in arguments");
                          }
                      }

                      
          //獲取BeanDefinition
                      RootBeanDefinition mergedBeanDefinition = getMergedBeanDefinition(beanName, false);
                      checkMergedBeanDefinition(mergedBeanDefinition, beanName, requiredType, args);

                      
          // Create bean instance.
                      
          //創建bean,如果為為singleton
                      if (mergedBeanDefinition.isSingleton()) {
                          
          synchronized (this.singletonCache) {
                              
          // Re-check singleton cache within synchronized block.
                              sharedInstance = this.singletonCache.get(beanName);
                              
          if (sharedInstance == null) {
                                  
          if (logger.isDebugEnabled()) {
                                      logger.debug(
          "Creating shared instance of singleton bean '" + beanName + "'");
                                  }
                                  
          this.currentlyInCreation.add(beanName);
                                  
          try {
                                      sharedInstance 
          = createBean(beanName, mergedBeanDefinition, args);
                                      //加進單例緩存
                                      addSingleton(beanName, sharedInstance);
                                  }
                                  
          catch (BeansException ex) {
                                      
          // Explicitly remove instance from singleton cache: It might have been put there
                                      
          // eagerly by the creation process, to allow for circular reference resolution.
                                      
          // Also remove any beans that received a temporary reference to the bean.
                                      destroyDisposableBean(beanName);
                                      
          throw ex;
                                  }
                                  
          finally {
                                      
          this.currentlyInCreation.remove(beanName);
                                  }
                              }
                          }
                          bean 
          = getObjectForSharedInstance(name, sharedInstance);
                      }
                      
          //如果是prototype
                      else {
                          
          // It's a prototype -> create a new instance.
                          bean = createBean(beanName, mergedBeanDefinition, args);
                      }
                  }

                  
          // Check if required type matches the type of the actual bean instance.
                  if (requiredType != null && !requiredType.isAssignableFrom(bean.getClass())) {
                      
          throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
                  }
                  
          return bean;
              }
              當你調用getBean獲取一個bean的時候,spring首先查找單例緩存中是否已經有這個bean,有的話直接返回(首先會判斷是否已經創建),如果沒有,spring就開始創建這個bean:首先獲取bean的定義(BeanDefinition),檢測bean是否定義在父工廠中,有的話調用父工廠的getBean方法;沒有就檢測bean是singleton還是prototype,如果是singleton就是創建bean并加入緩存以便下次直接調用,如果是prototype,就在每次調用時重新創建一個bean實例。注意createBean(beanName, mergedBeanDefinition, args); 這個方法,這是創建bean的核心方法,并且是一個abstract方法,將被子類實現。
              看看是如何獲取bean的定義的,
          protected RootBeanDefinition getMergedBeanDefinition(String name, boolean includingAncestors)
                  
          throws BeansException {

                  String beanName 
          = transformedBeanName(name);

                  
          // Efficiently check whether bean definition exists in this factory.
                  if (includingAncestors && !containsBeanDefinition(beanName) &&
                          getParentBeanFactory() 
          instanceof AbstractBeanFactory) {
                      
          return ((AbstractBeanFactory) getParentBeanFactory()).getMergedBeanDefinition(beanName, true);
                  }

                  
          // Resolve merged bean definition locally.
                  return getMergedBeanDefinition(beanName, getBeanDefinition(beanName));
              }

              調用重載的getMergedBeanDefinition合并父工廠和子工廠中的bean定義,注意getBeanDefinition(beanName),這是一個抽象方法,延遲到子類實現以便提供獲取bean定義的具體方法,這個方法和 createBean 一樣都是template method模式的應用。看看它們的定義:
          protected abstract BeanDefinition getBeanDefinition(String beanName) throws BeansException;

          protected abstract Object createBean(
                      String beanName, RootBeanDefinition mergedBeanDefinition, Object[] args) 
          throws BeanCreationException;
           
              基本了解了AbstractBeanFactory 后,我們來看看它的子類的AbstractAutowireCapableBeanFactory ,這個類實現了createBean() ,在這個方法中我們將看到與上面bean的生命周期圖對應的bean的創建過程,英文注釋已經非常清楚:
          protected Object createBean(String beanName, RootBeanDefinition mergedBeanDefinition, Object[] args)
                      
          throws BeanCreationException {

                  
          if (logger.isDebugEnabled()) {
                      logger.debug(
          "Creating instance of bean '" + beanName +
                              
          "' with merged definition [" + mergedBeanDefinition + "]");
                  }

                  Object bean 
          = null;

                  
          // Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
                  if (mergedBeanDefinition.hasBeanClass()) {
                      bean 
          = applyBeanPostProcessorsBeforeInstantiation(mergedBeanDefinition.getBeanClass(), beanName);
                      
          if (bean != null) {
                          
          return bean;
                      }
                  }

                  
          // Guarantee initialization of beans that the current one depends on.
                  if (mergedBeanDefinition.getDependsOn() != null) {
                      
          for (int i = 0; i < mergedBeanDefinition.getDependsOn().length; i++) {
                          getBean(mergedBeanDefinition.getDependsOn()[i]);
                      }
                  }

                  BeanWrapper instanceWrapper 
          = null;
                  Object originalBean 
          = null;
                  String errorMessage 
          = null;

                  
          try {
                      
          // Instantiate the bean.
                      errorMessage = "Instantiation of bean failed";

                      
          if (mergedBeanDefinition.getFactoryMethodName() != null)  {
                          instanceWrapper 
          = instantiateUsingFactoryMethod(beanName, mergedBeanDefinition, args);
                      }
                      
          else if (mergedBeanDefinition.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_CONSTRUCTOR ||
                              mergedBeanDefinition.hasConstructorArgumentValues() )  {
                          instanceWrapper 
          = autowireConstructor(beanName, mergedBeanDefinition);
                      }
                      
          else {
                          
          // No special handling: simply use no-arg constructor.
                          instanceWrapper = instantiateBean(beanName, mergedBeanDefinition);
                      }
                      bean 
          = instanceWrapper.getWrappedInstance();

                      
          // Eagerly cache singletons to be able to resolve circular references
                      
          // even when triggered by lifecycle interfaces like BeanFactoryAware.
                      if (isAllowCircularReferences() && isSingletonCurrentlyInCreation(beanName)) {
                          
          if (logger.isDebugEnabled()) {
                              logger.debug(
          "Eagerly caching bean with name '" + beanName +
                                      
          "' to allow for resolving potential circular references");
                          }
                          addSingleton(beanName, bean);
                      }

                      
          // Initialize the bean instance.
                      errorMessage = "Initialization of bean failed";
                      populateBean(beanName, mergedBeanDefinition, instanceWrapper);

                      
          if (bean instanceof BeanNameAware) {
                          
          if (logger.isDebugEnabled()) {
                              logger.debug(
          "Invoking setBeanName on BeanNameAware bean '" + beanName + "'");
                          }
                          ((BeanNameAware) bean).setBeanName(beanName);
                      }

                      
          if (bean instanceof BeanFactoryAware) {
                          
          if (logger.isDebugEnabled()) {
                              logger.debug(
          "Invoking setBeanFactory on BeanFactoryAware bean '" + beanName + "'");
                          }
                          ((BeanFactoryAware) bean).setBeanFactory(
          this);
                      }

                      originalBean 
          = bean;
                      bean 
          = applyBeanPostProcessorsBeforeInitialization(bean, beanName);
                      invokeInitMethods(beanName, bean, mergedBeanDefinition);
                      bean 
          = applyBeanPostProcessorsAfterInitialization(bean, beanName);
                  }
                  
          catch (BeanCreationException ex) {
                      
          throw ex;
                  }
                  
          catch (Throwable ex) {
                      
          throw new BeanCreationException(
                              mergedBeanDefinition.getResourceDescription(), beanName, errorMessage, ex);
                  }

                  
          // Register bean as disposable, and also as dependent on specified "dependsOn" beans.
                  registerDisposableBeanIfNecessary(beanName, originalBean, mergedBeanDefinition);

                  
          return bean;
              }
              通過instanceof操作符來判斷bean是否實現了用于生命周期回調的接口,然后調用相應的回調方法,可以看到spring充分實踐了針對接口編程的原則,雖然很夸張的一個方法一個接口的地步,不過這些接口也是作為mark interface用于標記回調。結合上面的生命周期圖看這段代碼將很好理解。有了bean的創建方法,那么如何獲取bean在配置文件中的定義信息呢?也就是在哪里實現了 getBeanDefinition方法呢?答案就在AbstractAutowireCapableBeanFactory 的子類DefaultListableBeanFactory中。

              DefaultListableBeanFactory 不僅繼承了AbstractAutowireCapableBeanFactory ,還實現了BeanDefinitionRegistry和ConfigurableListableBeanFactory接口。其中ConfigurableListableBeanFactory接口是定義了分析和修改bean定義信息的回調方法,暫時不去管它??纯碆eanDefinitionRegistry接口:
          public interface BeanDefinitionRegistry {
            
          int getBeanDefinitionCount();
            String[] getBeanDefinitionNames();
            BeanDefinition getBeanDefinition(String beanName) 
          throws NoSuchBeanDefinitionException;
            String[] getAliases(String beanName) 
          throws NoSuchBeanDefinitionException;
            
          void registerAlias(String beanName, String alias) throws BeansException;
            
          void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
                      
          throws BeansException;
            ......
          }

          一系列用于獲取bean定義信息的方法,這個接口我們將在后面的代碼中看到,作為一個mark inteface。注意咯,這個接口中非常關鍵的一個方法就是registerBeanDefinition,這個方法用于向bean工廠注冊解析的每個bean定義,我們將在xml文件的解析的環節看到調用這個方法注冊bean定義信息。

              DefaultListableBeanFactory實現了BeanDefinitionRegistry  接口,可以看到它實現了關鍵的registerBeanDefinition用于將bean的定義注冊到工廠類中,其中維護了一個Map用于存儲bean的定義信息:
          public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
                      
          throws BeanDefinitionStoreException {

                  Assert.hasText(beanName, 
          "Bean name must not be empty");
                  Assert.notNull(beanDefinition, 
          "Bean definition must not be null");

                  
          if (beanDefinition instanceof AbstractBeanDefinition) {
                      
          try {
                          ((AbstractBeanDefinition) beanDefinition).validate();
                      }
                      
          catch (BeanDefinitionValidationException ex) {
                          
          throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
                                  
          "Validation of bean definition failed", ex);
                      }
                  }

                  Object oldBeanDefinition 
          = this.beanDefinitionMap.get(beanName);
                  
          if (oldBeanDefinition != null) {
                      
          if (!isAllowBeanDefinitionOverriding()) {
                          
          throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
                                  
          "Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
                                  
          "': there's already [" + oldBeanDefinition + "] bound");
                      }
                      
          else {
                          
          if (logger.isInfoEnabled()) {
                              logger.info(
          "Overriding bean definition for bean '" + beanName +
                                      
          "': replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]");
                          }
                      }
                  }
                  
          else {
                      
          this.beanDefinitionNames.add(beanName);
                  }
                  
          this.beanDefinitionMap.put(beanName, beanDefinition);

                  
          // Remove corresponding bean from singleton cache, if any.
                  
          // Shouldn't usually be necessary, rather just meant for overriding
                  
          // a context's default beans (e.g. the default StaticMessageSource
                  
          // in a StaticApplicationContext).
                  removeSingleton(beanName);
              }
              beanDefinitionMap就是我們存儲工廠類中注冊的bean的信息,用bean name做為key:
          /** Map of bean definition objects, keyed by bean name */
              
          private final Map beanDefinitionMap = new HashMap();
           
              DefaultListableBeanFactory 重寫了
          protected abstract BeanDefinition getBeanDefinition(String beanName) throws BeansException;
          模板方法用于提供bean定義信息給getBean方法用于創建bean實例。getBeanDefinition的從beanDefinitionMap 查找bean定義即可:
          public BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException {
                  BeanDefinition bd 
          = (BeanDefinition) this.beanDefinitionMap.get(beanName);
                  
          if (bd == null) {
                      
          if (logger.isDebugEnabled()) {
                          logger.debug(
          "No bean named '" + beanName + "' found in " + toString());
                      }
                      
          throw new NoSuchBeanDefinitionException(beanName);
                  }
                  
          return bd;
              }

              看到了這里是不是覺的有點糊涂,我還是建議有興趣地找來spring1.2的源碼來看看就很清晰了,充分利用eclipse的F3跳轉結合上面的那張UML圖。
              OK,有了注冊bean定義信息的方法(registerBeanDefinition),有了獲取bean定義信息的方法(getBeanDefinition ),有了創建bean的方法(createBean),那么在哪里調用這些方法呢?createBean我們已經知道是在BeanFactory的getBean方法時調用,getBeanDefinition 又是在creatBean時調用用于獲取bean的定義信息,那么注冊bean定義信息的方法肯定是在xml文件解析階段被調用。
             XmlBeanFactory繼承DefaultListableBeanFactory ,沒有定義新的方法,只是有兩個重載的構造函數:
            
          public class XmlBeanFactory extends DefaultListableBeanFactory {
            
          private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);
            
          public XmlBeanFactory(Resource resource) throws BeansException {
                  
          this(resource, null);
              }
            
          public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
                  
          super(parentBeanFactory);
                  
          this.reader.loadBeanDefinitions(resource);
              }
          }

              初始化XmlBeanFactory時調用XmlBeanDefinitionReader讀取xml配置文件,而XmlBeanDefinitionReader的loadBeanDefinitions方法,載入xml文件并且調用XmlBeanDefinitionParser 解析xml文件同時注冊bean定義信息到bean工廠,這幾個類的協作以及它們繼承體系也非常清晰,在此不再詳述(累?。琗mlBeanDefinitionParser是一個接口,具體的實現類是org.springframework.beans.factory.xml.DefaultXmlBeanDefinitionParser,我們關注的就是它是怎么注冊bean信息到bean工廠的:
          protected int parseBeanDefinitions(Element root) throws BeanDefinitionStoreException {

              
              
          if (IMPORT_ELEMENT.equals(node.getNodeName())) {
                              importBeanDefinitionResource(ele);
                          }
                          
          else if (ALIAS_ELEMENT.equals(node.getNodeName())) {
                              String name 
          = ele.getAttribute(NAME_ATTRIBUTE);
                              String alias 
          = ele.getAttribute(ALIAS_ATTRIBUTE);
                              
          this.beanDefinitionReader.getBeanFactory().registerAlias(name, alias);
                          }
                          
          else if (BEAN_ELEMENT.equals(node.getNodeName())) {
                              beanDefinitionCount
          ++;
                              BeanDefinitionHolder bdHolder 
          = parseBeanDefinitionElement(ele, false);
                              
          //關鍵代碼,調用工具類將bean定義注冊到beanFactory
                              BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, this.beanDefinitionReader.getBeanFactory());
                          }
             
          }
          工具類BeanDefinitionReaderUtils中的注冊方法:
              public static void registerBeanDefinition(
                      BeanDefinitionHolder bdHolder, BeanDefinitionRegistry beanFactory) 
          throws BeansException {

                  
          // Register bean definition under primary name.,注冊bean
                  beanFactory.registerBeanDefinition(bdHolder.getBeanName(), bdHolder.getBeanDefinition());

                  
          // Register aliases for bean name, if any.,注冊別名
                  if (bdHolder.getAliases() != null) {
                      
          for (int i = 0; i < bdHolder.getAliases().length; i++) {
                          beanFactory.registerAlias(bdHolder.getBeanName(), bdHolder.getAliases()[i]);
                      }
                  }
              }
             注意這里的beanFactory是BeanDefinitionRegistry類型 ,DefaultListableBeanFactory實現了這個接口。
              更多內容,比如spring是怎么把bean的屬性注入的?答案在BeanWrapperImpl中的一系列setPropertyXXX方法。BeanFactory是如何實例化bean的呢?具體展開請看org.springframework.beans.factory.support.InstantiationStrategy 接口,一個典型的策略模式的應用,兩個實現類:SimpleInstantiationStrategy和CglibSubclassingInstantiationStrategy。我們知道spring有3種注入方式:setter、構造函數以及Method Inject。這里的CglibSubclassingInstantiationStrategy使用cglib庫用于實現method inject。這樣一篇小文容不下這么多內容,還是有興趣的自己找源碼讀讀。

              寫到這里,我對自己的文字表達能力產生懷疑,我能不能把這些所有的信息都說清楚呢?感覺很不滿意,將就吧,也算是自己的學習總結。
              讀spring源碼這段時間,最深的幾點體會:
          1)單元測試非常重要,充分感受到了單元測試作為項目文檔的優勢。看不懂一個類,找到這個類的測試類就OK!
          2)針對接口編程的原則,spring處處體現了這個原則,繁多的接口帶來的是松耦合、高內聚并且易于擴展的架構,但也帶來了一定的理解難度。作為通用型框架也許需要這么考慮,在實際項目中的取舍還是要自己把握。
          3)設計模式的使用,spring中應用了很多模式,比如template method,比如strategy和factory method、visitor、singleton等,簡直是一本模式實踐的良好教材。

           
             


          評論

          # re: spring IOC容器實現探討  回復  更多評論   

          2007-04-20 15:16 by 天塵
          啊,太棒了!受益很深!謝謝

          # re: spring IOC容器實現探討  回復  更多評論   

          2007-04-20 15:18 by 匿名
          樓主的筆記做的太好了spring代碼偶也讀過,太繁雜就看不下去了。

          # re: spring IOC容器實現探討  回復  更多評論   

          2007-04-20 15:32 by 山風小子
          師傅領進門,修行靠自身。
          謝啦 :)

          # re: spring IOC容器實現探討  回復  更多評論   

          2007-04-20 17:19 by dennis

          @天塵
          呵呵,謝謝,我描述的并不是很清楚,還是建議自己結合著看源碼

          # re: spring IOC容器實現探討  回復  更多評論   

          2007-04-20 17:19 by dennis
          @山風小子
          受之有愧,能有幫助是我的榮幸。

          # re: spring IOC容器實現探討[未登錄]  回復  更多評論   

          2007-04-21 10:42 by flyhawk1010
          寫的不錯,加油

          # re: spring IOC容器實現探討  回復  更多評論   

          2007-05-18 08:48 by hidder
          先看了前面2小段,我對Spring IOC這一部分已經使用了多次,不過一直沒有時間靜下來好好研究下作者的設計,現在看了你的文章再來研究研究,相信迅速的躲

          # re: spring IOC容器實現探討  回復  更多評論   

          2010-01-13 10:22 by 匿名
          ddddddddddd

          # re: spring IOC容器實現探討  回復  更多評論   

          2010-01-13 10:25 by 匿名
          什么時候國人能自己寫出像spring這樣的東西呀

          # re: spring IOC容器實現探討  回復  更多評論   

          2011-03-07 21:25 by laimuc
          頂一個

          # re: spring IOC容器實現探討  回復  更多評論   

          2012-06-27 17:32 by RoD
          確實還不錯

          # re: spring IOC容器實現探討[未登錄]  回復  更多評論   

          2013-03-30 20:11 by Java愛好者
          Spring的代碼內部結構比較復雜,把握起來比較困難,老外的思維復雜!

          下面一個Ioc容器,其邏輯結構清晰,非常容易掌握,代碼可讀性也比較好!

          http://dl.vmall.com/c0an2yalhj

          優點:接口定義合理, 邏輯脈絡清晰,非常容易學習與掌握, 100%緣創!

          重要的是該工具為國人于07年完成大部分開發,只不過沒有大面積宣傳而已,

          就算宣傳,估計也不會被國人認可的!




          主站蜘蛛池模板: 志丹县| 神农架林区| 象州县| 大理市| 明溪县| 安龙县| 蒙城县| 安陆市| 合江县| 宁南县| 泰安市| 潼南县| 民丰县| 盘山县| 巴彦淖尔市| 准格尔旗| 手机| 凤冈县| 原阳县| 南平市| 修武县| 房山区| 自贡市| 杭州市| 卓资县| 贡觉县| 阿尔山市| 通山县| 芷江| 温州市| 南华县| 图片| 嵊泗县| 林周县| 彭水| 专栏| 浪卡子县| 芮城县| 遵化市| 鄯善县| 工布江达县|