spring-DispatcherServlet初始化之路

(1)StandardWrapper調用DispatcherServlet的init()方法
(1.1)執行DispatcherServlet構造方法,從GenericServlet-HttpServlet-HttpServletBean-FrameworkServlet-DispatcherServlet都實例化了
(1.2)執行DispatcherServlet的static代碼塊

static {
        //如果在web.xml中沒有配置DispatcherServlet的配置文件地址,默認加載DispatcherServlet.properties
        //defaultStrategies  類型是 Properties
        try {
            ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class);
            defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
        }
        catch (IOException ex) {
            throw new IllegalStateException("Could not load 'DispatcherServlet.properties': " + ex.getMessage());
        }
    }

(2)DispatcherServlet沒有重寫init(ServletConfig config)方法,所以調用基類GenericServlet的init(ServletConfig config)方法

public void init(ServletConfig config) throws ServletException {
    //這裏的config就是StandardWrapper包裝成的SrandardWrapperFacade
    this.config = config;
    this.init();
    }

(2.1)HttpServletBean的init()方法
(2.2)FrameworkServlet的initServletBean()方法
(2.3)initWebApplicationContext 初始化一個WebApplicationContext(高級IOC容器)

protected WebApplicationContext initWebApplicationContext() {
        WebApplicationContext rootContext =
                WebApplicationContextUtils.getWebApplicationContext(getServletContext());
        WebApplicationContext wac = null;

        if (this.webApplicationContext != null) {
            // A context instance was injected at construction time -> use it
            wac = this.webApplicationContext;
            if (wac instanceof ConfigurableWebApplicationContext) {
                ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
                if (!cwac.isActive()) {
                    // The context has not yet been refreshed -> provide services such as
                    // setting the parent context, setting the application context id, etc
                    if (cwac.getParent() == null) {
                        // The context instance was injected without an explicit parent -> set
                        // the root application context (if any; may be null) as the parent
                        cwac.setParent(rootContext);
                    }
                    configureAndRefreshWebApplicationContext(cwac);
                }
            }
        }
        if (wac == null) {
            // No context instance was injected at construction time -> see if one
            // has been registered in the servlet context. If one exists, it is assumed
            // that the parent context (if any) has already been set and that the
            // user has performed any initialization such as setting the context id
            wac = findWebApplicationContext();
        }
        //上面都是判斷有沒有已經存在的webApplication有就初始化,沒有就這裏創建一個
        if (wac == null) {
            // No context instance is defined for this servlet -> create a local one
            wac = createWebApplicationContext(rootContext);
        }

        if (!this.refreshEventReceived) {
            // Either the context is not a ConfigurableApplicationContext with refresh
            // support or the context injected at construction time had already been
            // refreshed -> trigger initial onRefresh manually here.
            onRefresh(wac);
        }

        if (this.publishContext) {
            // Publish the context as a servlet context attribute.
            String attrName = getServletContextAttributeName();
            getServletContext().setAttribute(attrName, wac);
            if (this.logger.isDebugEnabled()) {
                this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() +
                        "' as ServletContext attribute with name [" + attrName + "]");
            }
        }

        return wac;
    }

(2.4)createWebApplicationContext() 創建IOC容器 這個過程中會對當前ioc容器進行屬性方面的初始化,例如:parent,Environment,ConfigLocation,etc

(2.5)來到 FrameworkServlet的 configureAndRefreshWebApplicationContext()中調用WebapplicationContext的refresh方法進行ioc容器初始化
ioc容器的初始化一般來說分爲3個步驟
1.Resource的定位
2.BeanDefinition(對象依賴關係數據的抽象)的解析和載入
3.向IoC容器註冊這些BeanDifinition

用編程式使用IoC容器來理解Resource、BeanFactory、BeanDifinitionReader之間的相互關係

//創建IoC配置文件的抽象資源,這個抽象資源中包含了BeanDifinition的定義信息
ClassPathResouce res = new ClassPathResource("bean.xml");
//創建一個BeanFactory
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
//創建一個載入BeanDefinition的讀取器,通過一個回調配置給BeanFactory
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
//從定義好的資源位置讀入配置信息,具體的解析過程由reader來完成
reader.loadBeanDefinitions(res);

(3)AbstractApplication 的 refresh()方法 – obtainFreshBeanFactory()

(3.1)AbstractRefreshableApplicationContext的refreshBeanFactory()方法

protected final void refreshBeanFactory() throws BeansException {
        //查看有沒有已經從創建的BeanFactory,有就銷燬掉
        if (hasBeanFactory()) {
            destroyBeans();
            closeBeanFactory();
        }
        try {
            //在這裏創建一個新的BeanFactory
            //DefaultListableBeanFactory是一個最基礎的IOC容器
            DefaultListableBeanFactory beanFactory = createBeanFactory();
            beanFactory.setSerializationId(getId());
            customizeBeanFactory(beanFactory);
            loadBeanDefinitions(beanFactory);
            synchronized (this.beanFactoryMonitor) {
                this.beanFactory = beanFactory;
            }
        }
        catch (IOException ex) {
            throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
        }
    }

(3.2)XmlWebApplication的loadBeanDefinitions(DefaultListableBeanFactory beanFactory)

@Override
    protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
        // Create a new XmlBeanDefinitionReader for the given BeanFactory.
        XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

        // Configure the bean definition reader with this context's
        // resource loading environment.
        beanDefinitionReader.setEnvironment(getEnvironment());
        beanDefinitionReader.setResourceLoader(this);
        beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

        // Allow a subclass to provide custom initialization of the reader,
        // then proceed with actually loading the bean definitions.
        initBeanDefinitionReader(beanDefinitionReader);
        loadBeanDefinitions(beanDefinitionReader);
    }
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException {
        //這裏調用getConfigLocations()如果配置的定位爲空就得到得到"spring-servlet.xml"
        String[] configLocations = getConfigLocations();
        if (configLocations != null) {
            for (String configLocation : configLocations) {
                reader.loadBeanDefinitions(configLocation);
            }
        }
    }

(3.3)之後就一個一個循環configLocations 用location構建Resource,然後將Resource轉換成EncodedResource

(3.4)XmlBeanDefinitionReader

public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
        Assert.notNull(encodedResource, "EncodedResource must not be null");
        if (logger.isInfoEnabled()) {
            logger.info("Loading XML bean definitions from " + encodedResource.getResource());
        }

        Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
        if (currentResources == null) {
            currentResources = new HashSet<EncodedResource>(4);
            this.resourcesCurrentlyBeingLoaded.set(currentResources);
        }
        if (!currentResources.add(encodedResource)) {
            throw new BeanDefinitionStoreException(
                    "Detected cyclic loading of " + encodedResource + " - check your import definitions!");
        }
        try {
            //打開Resource的IO流,準備讀取BeanDefinition
            InputStream inputStream = encodedResource.getResource().getInputStream();
            try {
                InputSource inputSource = new InputSource(inputStream);
                if (encodedResource.getEncoding() != null) {
                    inputSource.setEncoding(encodedResource.getEncoding());
                }
                //通過以上步驟,IoC容器初始化的第一個步驟就已經完成了,這裏是第二個步驟的入口,BeanDefinition的解析和載入
                return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
            }
            finally {
                inputStream.close();
            }
        }
        catch (IOException ex) {
            throw new BeanDefinitionStoreException(
                    "IOException parsing XML document from " + encodedResource.getResource(), ex);
        }
        finally {
            currentResources.remove(encodedResource);
            if (currentResources.isEmpty()) {
                this.resourcesCurrentlyBeingLoaded.remove();
            }
        }
    }

(3.5)XmlWebApplicationReader

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
            throws BeanDefinitionStoreException {
        try {
            //這裏得到XML的Document對象,這個解析過程是由documentLoader完成的
            Document doc = doLoadDocument(inputSource, resource);
            //Spring的BeanDefinition是如何按照Spring中的Bean語義要求進行解析
            //並轉化爲容器的內部數據結構的,過程就在這之中
            return registerBeanDefinitions(doc, resource);
        }
        catch (BeanDefinitionStoreException ex) {
            throw ex;
        }
        catch (SAXParseException ex) {
            throw new XmlBeanDefinitionStoreException(resource.getDescription(),
                    "Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex);
        }
        catch (SAXException ex) {
            throw new XmlBeanDefinitionStoreException(resource.getDescription(),
                    "XML document from " + resource + " is invalid", ex);
        }
        catch (ParserConfigurationException ex) {
            throw new BeanDefinitionStoreException(resource.getDescription(),
                    "Parser configuration exception parsing XML from " + resource, ex);
        }
        catch (IOException ex) {
            throw new BeanDefinitionStoreException(resource.getDescription(),
                    "IOException parsing XML document from " + resource, ex);
        }
        catch (Throwable ex) {
            throw new BeanDefinitionStoreException(resource.getDescription(),
                    "Unexpected exception parsing XML document from " + resource, ex);
        }
    }

(3.6)XmlWebApplicationReader

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
        //具體的解析是由BeanDefinitionDocumentReader來完成的
        BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
        int countBefore = getRegistry().getBeanDefinitionCount();
        //這裏面是具體過程
        documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
        return getRegistry().getBeanDefinitionCount() - countBefore;
    }

(3.7)之後會得到一個BeanDefinitionParserDelegate 和Beandefinition的root元素

(3.8)一直來到DefaultBeanDefinitionDocumentReader的 parseBeanDefinitions方法中

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
        //這裏會從根節點開始一行一行一個節點一個節點地往下解析
        if (delegate.isDefaultNamespace(root)) {
            NodeList nl = root.getChildNodes();
            for (int i = 0; i < nl.getLength(); i++) {
                Node node = nl.item(i);
                //如果該節點是元素類型
                if (node instanceof Element) {
                    Element ele = (Element) node;
                    //判斷該元素是本文件的命名空間中命名的元素還是其他命名空間中的元素,
                    if (delegate.isDefaultNamespace(ele)) {
                        //解析本文件中的元素,該方法中會判斷是什麼節點,然後調用具體的解析方法
                        parseDefaultElement(ele, delegate);
                    }
                    else {
                        //其他命名空間元素
                        delegate.parseCustomElement(ele);
                    }
                }
            }
        }
        else {
            delegate.parseCustomElement(root);
        }
    }

(3.9)具體的解析過程由具體的節點決定,有很多種,我這裏就不一一羅列出來了,就貼一個<\import>解析過程

protected void importBeanDefinitionResource(Element ele) {
        String location = ele.getAttribute(RESOURCE_ATTRIBUTE);
        if (!StringUtils.hasText(location)) {
            getReaderContext().error("Resource location must not be empty", ele);
            return;
        }

        // Resolve system properties: e.g. "${user.dir}"
        location = getReaderContext().getEnvironment().resolveRequiredPlaceholders(location);

        Set<Resource> actualResources = new LinkedHashSet<Resource>(4);

        // Discover whether the location is an absolute or relative URI
        boolean absoluteLocation = false;
        try {
            absoluteLocation = ResourcePatternUtils.isUrl(location) || ResourceUtils.toURI(location).isAbsolute();
        }
        catch (URISyntaxException ex) {
            // cannot convert to an URI, considering the location relative
            // unless it is the well-known Spring prefix "classpath*:"
        }

        // Absolute or relative?
        if (absoluteLocation) {
            try {
                int importCount = getReaderContext().getReader().loadBeanDefinitions(location, actualResources);
                if (logger.isDebugEnabled()) {
                    logger.debug("Imported " + importCount + " bean definitions from URL location [" + location + "]");
                }
            }
            catch (BeanDefinitionStoreException ex) {
                getReaderContext().error(
                        "Failed to import bean definitions from URL location [" + location + "]", ele, ex);
            }
        }
        else {
            // No URL -> considering resource location as relative to the current file.
            try {
                int importCount;
                Resource relativeResource = getReaderContext().getResource().createRelative(location);
                if (relativeResource.exists()) {
                    importCount = getReaderContext().getReader().loadBeanDefinitions(relativeResource);
                    actualResources.add(relativeResource);
                }
                else {
                    String baseLocation = getReaderContext().getResource().getURL().toString();
                    importCount = getReaderContext().getReader().loadBeanDefinitions(
                            StringUtils.applyRelativePath(baseLocation, location), actualResources);
                }
                if (logger.isDebugEnabled()) {
                    logger.debug("Imported " + importCount + " bean definitions from relative location [" + location + "]");
                }
            }
            catch (IOException ex) {
                getReaderContext().error("Failed to resolve current resource location", ele, ex);
            }
            catch (BeanDefinitionStoreException ex) {
                getReaderContext().error("Failed to import bean definitions from relative location [" + location + "]",
                        ele, ex);
            }
        }
        Resource[] actResArray = actualResources.toArray(new Resource[actualResources.size()]);
        getReaderContext().fireImportProcessed(location, actResArray, extractSource(ele));
    }

(4)在所有IoC初始化完成之後會返回到FrameworkServlet中調用onRefresh(wac);

(5)DispatcherServlet的onRefresh方法

protected void onRefresh(ApplicationContext context) {
        initStrategies(context);
    }
//在這裏完成servlet中各種組件的初始化
protected void initStrategies(ApplicationContext context) {
        initMultipartResolver(context);
        initLocaleResolver(context);
        initThemeResolver(context);
        initHandlerMappings(context);
        initHandlerAdapters(context);
        initHandlerExceptionResolvers(context);
        initRequestToViewNameTranslator(context);
        initViewResolvers(context);
        initFlashMapManager(context);
    }

這些是我個人的一些認識,很淺薄,希望以後還可以更加深入,然後來修改這篇博客。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章