SpringMVC源码解析(1):DispatcherServlet的初始化源码之Bean加载

         SpringMVC是当下使用量比较大的web端框架,为了增加自己对SpringMVC的深入理解,故浅度剖析框架源码。如博客中存在任何问题,欢迎您指正。

一 ,概述

DispatcherServlet是SpringMVC框架的入口,而servlet有其自己的生命周期。初始化对应的方法是init,因此我们从该方法入手进行SpringMVC初始化的源码阅读。

 

二 ,核心类继承关系图

                                   

DispatcherServlet的类继承结构图如上所示,关于servlet的核心方法:init/service/destory 可以直接在父类中找到。而关于Aware接口的部分,主要是抽象设置ApplicationContext和Environment的方法,可以在如上的FrameworkServlet和HttpServletBean中看到具体实现。

 

三 ,源码分析

说明:因为框架中有很多地方的代码就自己目前的水平没有办法持续深究,所以这边展示的是对主要方法的理解,当然为了节约篇幅也会省略其他的无关代码。本篇博客主要描述SpringMVC加载bean的过程,代码跳转的过程可以通过黑体标注查看。如有问题,还望指正。

org.springframework.web.servlet.HttpServletBean--> init()
@Override
public final void init() throws ServletException {
  ......
   // Set bean properties from init parameters.Get Config location and other param.
   ...
      PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
      BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
      ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
      bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
      initBeanWrapper(bw);
      bw.setPropertyValues(pvs, true);
  ...
   //The place of main load xml bean and init servlet opration.
   initServletBean();
......
}

org.springframework.web.servlet.FrameworkServlet--> initServletBean()
@Override
protected final void initServletBean() throws ServletException {
  ......
      this.webApplicationContext = initWebApplicationContext();
      initFrameworkServlet();
  ......
}
org.springframework.web.servlet.FrameworkServlet--> initWebApplicationContext()
protected WebApplicationContext initWebApplicationContext() {
//create WebApplicationContext object.
  ......
   if (this.webApplicationContext != null) {
      wac = this.webApplicationContext;
      if (wac instanceof ConfigurableWebApplicationContext) {
         ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
         if (!cwac.isActive()) {
            if (cwac.getParent() == null) {
               cwac.setParent(rootContext);
            }
//Config and Refresh object of creator,including spring bean load.
            configureAndRefreshWebApplicationContext(cwac);
         }
      }
   }
  ......
   return wac;
}
下一篇博客会看一下这个Listener所带来的初始化作用,本次着重spring bean的初始化
org.springframework.web.servlet.FrameworkServlet--> configureAndRefreshWebApplicationContext()
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
   ......    
//Add a listener for context refresh,it will invoke method named onRefresh(),which define in current class.
    wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));
   ......
   postProcessWebApplicationContext(wac);
   applyInitializers(wac);
//This method can show us how to load object which managed by spring.
   wac.refresh();
}
org.springframework.context.support.AbstractApplicationContext--> refresh()
@Override
public void refresh() throws BeansException, IllegalStateException {
   synchronized (this.startupShutdownMonitor) {
      prepareRefresh();
      // Real Bean Load Process is in following method.
      ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
......
   }
}
org.springframework.context.support.AbstractApplicationContext--> obtainFreshBeanFactory()
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
   refreshBeanFactory();
   ConfigurableListableBeanFactory beanFactory = getBeanFactory();
  ......
   return beanFactory;
} 

org.springframework.context.support.AbstractRefreshableApplicationContext--> refreshBeanFactory()
@Override
protected final void refreshBeanFactory() throws BeansException {
  ......
      DefaultListableBeanFactory beanFactory = createBeanFactory();
      beanFactory.setSerializationId(getId());
      customizeBeanFactory(beanFactory);
//Through bean factory to load bean definiton,So we can check bean factory filed like following.
      loadBeanDefinitions(beanFactory);
      synchronized (this.beanFactoryMonitor) {
         this.beanFactory = beanFactory;
      }
   ......
}
org.springframework.context.support.AbstractXmlApplicationContext--> loadBeanDefinitions(beanFactory)
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
   // Create a new XmlBeanDefinitionReader for the given BeanFactory.
   XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
......
   initBeanDefinitionReader(beanDefinitionReader);
   loadBeanDefinitions(beanDefinitionReader);
}
org.springframework.context.support.AbstractXmlApplicationContext--> loadBeanDefinitions(reader)
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
   Resource[] configResources = getConfigResources();
   if (configResources != null) {
      reader.loadBeanDefinitions(configResources);
   }
  ......
}
org.springframework.beans.factory.support.AbstractBeanDefinitionReader--> loadBeanDefinitions()
@Override
public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
   Assert.notNull(resources, "Resource array must not be null");
   int counter = 0;
   for (Resource resource : resources) {
//keep skip to subclass.
      counter += loadBeanDefinitions(resource);
   }
   return counter;
}
org.springframework.beans.factory.xml.XmlBeanDefinitionReader--> loadBeanDefinitions()
@Override
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
//Load bean from xmll file which config in web.xml file.
   return loadBeanDefinitions(new EncodedResource(resource));
}
org.springframework.beans.factory.xml.XmlBeanDefinitionReader--> loadBeanDefinitions(encodedResource)
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
  ......
      InputStream inputStream = encodedResource.getResource().getInputStream();
      try {
         InputSource inputSource = new InputSource(inputStream);
         if (encodedResource.getEncoding() != null) {
            inputSource.setEncoding(encodedResource.getEncoding());
         }
         return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
     }
     ......
}
org.springframework.beans.factory.xml.XmlBeanDefinitionReader--> doLoadBeanDefinitions()
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
   ......
//Use w3c document object to parse xml file
      Document doc = doLoadDocument(inputSource, resource);
//bean load from document object
      return registerBeanDefinitions(doc, resource);
   ......
}
org.springframework.beans.factory.xml.XmlBeanDefinitionReader--> doLoadDocument()
protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
//Parse different xml format file by different valifation mode.
   return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
         getValidationModeForResource(resource), isNamespaceAware());
}
protected int getValidationModeForResource(Resource resource) { //Inner method of load Doument.
   int validationModeToUse = getValidationMode();
   if (validationModeToUse != VALIDATION_AUTO) {
      return validationModeToUse;
   }
//This method will show us how to get validation mode of xml,including dtd/xsd,main method is through DOCTYPE,If you want //know ,you can get it by your own skip method.
   int detectedMode = detectValidationMode(resource);
   if (detectedMode != VALIDATION_AUTO) {
      return detectedMode;
   }
   return VALIDATION_XSD;
}
//start bean load.......
org.springframework.beans.factory.xml.XmlBeanDefinitionReader--> registerBeanDefinitions()
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
   BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
   documentReader.setEnvironment(this.getEnvironment());
   int countBefore = getRegistry().getBeanDefinitionCount();
   documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
   return getRegistry().getBeanDefinitionCount() - countBefore;
}

......Skip to final main method......
org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader -->parseBeanDefinitions()
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
//loop the xml object from root to children,and parse element.
   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);//this is customer element parse,it will be seen next time.
            }
         }
      }
   }
   else {
      delegate.parseCustomElement(root);
   }
}
org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader--> parseDefaultElement()
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
//There has four different element parse,So we can see all bean load logic in here.
   if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
      importBeanDefinitionResource(ele);
   }
   else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
      processAliasRegistration(ele);
   }
   else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
      processBeanDefinition(ele, delegate);
   }
   else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
      // recurse
      doRegisterBeanDefinitions(ele);
   }
}

未完待续......

 

四 ,疑惑点

1. Spring在解析XML文件之前会进行验证XML的格式是不是正确,代码是在getEntityResolver()中获取不同的EntityResolver 如:DelegatingEntityResolver中的getEntityResolver方法中,但是没有理清楚这个入口,后面会继续学习解释。

2.关于Spring对象的高级解析细节,如对象依赖,自定义标签等的深入理解,有待学习。

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值