<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    莊周夢蝶

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

    spring IOC容器實現(xiàn)探討

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

        AbstractBeanFactory作為BeanFactory接口的抽象實現(xiàn)類,是其他工廠類的父類,提供了bean的singlton緩存、singleton/prototype的決定、bean的別名以及bean和它的子類bean的定義合并(bean definition merging for child bean definitions)和銷毀。這里有3個重載的getBean方法(實現(xiàn)BeanFactory定義的getBean方法),我們關(guān)注下最主要的這個方法:
    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.
                
    //創(chuàng)建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;
        }
        當你調(diào)用getBean獲取一個bean的時候,spring首先查找單例緩存中是否已經(jīng)有這個bean,有的話直接返回(首先會判斷是否已經(jīng)創(chuàng)建),如果沒有,spring就開始創(chuàng)建這個bean:首先獲取bean的定義(BeanDefinition),檢測bean是否定義在父工廠中,有的話調(diào)用父工廠的getBean方法;沒有就檢測bean是singleton還是prototype,如果是singleton就是創(chuàng)建bean并加入緩存以便下次直接調(diào)用,如果是prototype,就在每次調(diào)用時重新創(chuàng)建一個bean實例。注意createBean(beanName, mergedBeanDefinition, args); 這個方法,這是創(chuàng)建bean的核心方法,并且是一個abstract方法,將被子類實現(xiàn)。
        看看是如何獲取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));
        }

        調(diào)用重載的getMergedBeanDefinition合并父工廠和子工廠中的bean定義,注意getBeanDefinition(beanName),這是一個抽象方法,延遲到子類實現(xiàn)以便提供獲取bean定義的具體方法,這個方法和 createBean 一樣都是template method模式的應用??纯此鼈兊亩x:
    protected abstract BeanDefinition getBeanDefinition(String beanName) throws BeansException;

    protected abstract Object createBean(
                String beanName, RootBeanDefinition mergedBeanDefinition, Object[] args) 
    throws BeanCreationException;
     
        基本了解了AbstractBeanFactory 后,我們來看看它的子類的AbstractAutowireCapableBeanFactory ,這個類實現(xiàn)了createBean() ,在這個方法中我們將看到與上面bean的生命周期圖對應的bean的創(chuàng)建過程,英文注釋已經(jīng)非常清楚:
    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是否實現(xiàn)了用于生命周期回調(diào)的接口,然后調(diào)用相應的回調(diào)方法,可以看到spring充分實踐了針對接口編程的原則,雖然很夸張的一個方法一個接口的地步,不過這些接口也是作為mark interface用于標記回調(diào)。結(jié)合上面的生命周期圖看這段代碼將很好理解。有了bean的創(chuàng)建方法,那么如何獲取bean在配置文件中的定義信息呢?也就是在哪里實現(xiàn)了 getBeanDefinition方法呢?答案就在AbstractAutowireCapableBeanFactory 的子類DefaultListableBeanFactory中。

        DefaultListableBeanFactory 不僅繼承了AbstractAutowireCapableBeanFactory ,還實現(xiàn)了BeanDefinitionRegistry和ConfigurableListableBeanFactory接口。其中ConfigurableListableBeanFactory接口是定義了分析和修改bean定義信息的回調(diào)方法,暫時不去管它??纯碆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。注意咯,這個接口中非常關(guān)鍵的一個方法就是registerBeanDefinition,這個方法用于向bean工廠注冊解析的每個bean定義,我們將在xml文件的解析的環(huán)節(jié)看到調(diào)用這個方法注冊bean定義信息。

        DefaultListableBeanFactory實現(xiàn)了BeanDefinitionRegistry  接口,可以看到它實現(xiàn)了關(guān)鍵的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方法用于創(chuàng)建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跳轉(zhuǎn)結(jié)合上面的那張UML圖。
        OK,有了注冊bean定義信息的方法(registerBeanDefinition),有了獲取bean定義信息的方法(getBeanDefinition ),有了創(chuàng)建bean的方法(createBean),那么在哪里調(diào)用這些方法呢?createBean我們已經(jīng)知道是在BeanFactory的getBean方法時調(diào)用,getBeanDefinition 又是在creatBean時調(diào)用用于獲取bean的定義信息,那么注冊bean定義信息的方法肯定是在xml文件解析階段被調(diào)用。
       XmlBeanFactory繼承DefaultListableBeanFactory ,沒有定義新的方法,只是有兩個重載的構(gòu)造函數(shù):
      
    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時調(diào)用XmlBeanDefinitionReader讀取xml配置文件,而XmlBeanDefinitionReader的loadBeanDefinitions方法,載入xml文件并且調(diào)用XmlBeanDefinitionParser 解析xml文件同時注冊bean定義信息到bean工廠,這幾個類的協(xié)作以及它們繼承體系也非常清晰,在此不再詳述(累?。?,XmlBeanDefinitionParser是一個接口,具體的實現(xiàn)類是org.springframework.beans.factory.xml.DefaultXmlBeanDefinitionParser,我們關(guān)注的就是它是怎么注冊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);
                        
    //關(guān)鍵代碼,調(diào)用工具類將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實現(xiàn)了這個接口。
        更多內(nèi)容,比如spring是怎么把bean的屬性注入的?答案在BeanWrapperImpl中的一系列setPropertyXXX方法。BeanFactory是如何實例化bean的呢?具體展開請看org.springframework.beans.factory.support.InstantiationStrategy 接口,一個典型的策略模式的應用,兩個實現(xiàn)類:SimpleInstantiationStrategy和CglibSubclassingInstantiationStrategy。我們知道spring有3種注入方式:setter、構(gòu)造函數(shù)以及Method Inject。這里的CglibSubclassingInstantiationStrategy使用cglib庫用于實現(xiàn)method inject。這樣一篇小文容不下這么多內(nèi)容,還是有興趣的自己找源碼讀讀。

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

     
       


    評論

    # re: spring IOC容器實現(xiàn)探討  回復  更多評論   

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

    # re: spring IOC容器實現(xiàn)探討  回復  更多評論   

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

    # re: spring IOC容器實現(xiàn)探討  回復  更多評論   

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

    # re: spring IOC容器實現(xiàn)探討  回復  更多評論   

    2007-04-20 17:19 by dennis

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

    # re: spring IOC容器實現(xiàn)探討  回復  更多評論   

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

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

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

    # re: spring IOC容器實現(xiàn)探討  回復  更多評論   

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

    # re: spring IOC容器實現(xiàn)探討  回復  更多評論   

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

    # re: spring IOC容器實現(xiàn)探討  回復  更多評論   

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

    # re: spring IOC容器實現(xiàn)探討  回復  更多評論   

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

    # re: spring IOC容器實現(xiàn)探討  回復  更多評論   

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

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

    2013-03-30 20:11 by Java愛好者
    Spring的代碼內(nèi)部結(jié)構(gòu)比較復雜,把握起來比較困難,老外的思維復雜!

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

    http://dl.vmall.com/c0an2yalhj

    優(yōu)點:接口定義合理, 邏輯脈絡清晰,非常容易學習與掌握, 100%緣創(chuàng)!

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

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




    主站蜘蛛池模板: 国产在线不卡免费播放| 亚洲成人免费在线| 色噜噜狠狠色综合免费视频 | 免费看美女裸露无档网站| 91精品国产免费久久国语蜜臀| 99久久免费国产特黄| 中国一级特黄的片子免费| 91在线免费视频| 久久国产乱子免费精品| 99免费在线观看视频| 在线永久免费的视频草莓| 97在线观免费视频观看| 日韩免费无砖专区2020狼| 免费大学生国产在线观看p| 亚洲第一区精品日韩在线播放| 亚洲无线一二三四区手机| 亚洲线精品一区二区三区影音先锋| 亚洲中文字幕无码一区| 久久精品国产亚洲香蕉| 亚洲一区二区在线免费观看| 7777久久亚洲中文字幕蜜桃| 亚洲小视频在线播放| 亚洲欧美日韩中文无线码| 激情婷婷成人亚洲综合| 一级毛片在线免费播放| 女人隐私秘视频黄www免费| 无码精品人妻一区二区三区免费看 | 亚洲AV无码专区在线厂| 男女猛烈激情xx00免费视频| 中文字幕免费观看全部电影| 亚欧免费无码aⅴ在线观看| 一个人免费高清在线观看| 四虎永久免费地址在线网站| 浮力影院亚洲国产第一页| 久久久影院亚洲精品| 7777久久亚洲中文字幕| 狠狠入ady亚洲精品| 两个人看的www免费视频中文| 51视频精品全部免费最新| 日韩在线看片免费人成视频播放| 久久影视国产亚洲|