zoukankan      html  css  js  c++  java
  • spring源码读书笔记

    如果我们在web项目里面使用spring的话,通常会在web.xml里面配置一个listener.

    <listener>

    <listener-class>

    org.springframework.web.context.ContextLoaderListener

    </listener-class>

    </listener>

    这个litener实现了ServletContextListener接口,并从ContextLoader继承。由于实现了ServletContextListener接口,所以在web容器启动的时候会调用contextInitialized方法。以下是这个方法的实现:

    public void contextInitialized(ServletContextEvent event) {

           //createContextLoader是一个废弃了的方法,什么也没有做。返回null值

           this.contextLoader = createContextLoader();

           //所以this.contextLoader为null,这里把自身赋值给contextLoader

    //因为ContextLoaderListerner继承了ContextLoader,所以可把自身赋值给

    //ContextLoader(这里有点别扭)

           if (this.contextLoader == null) {

               this.contextLoader = this;

           }

           //接着就实例化webApplicationContext,由父类ContextLoader实现

        this.contextLoader.initWebApplicationContext(event.getServletContext());

    }

    我们现在来看一下ContextLoader的initWebApplicationContext方法,这个方法比较长,我们分段逐步跟进去看一下。(会省略一些不重要的代码)

    以下这段代码主要判断是否重复实例化的问题,因为实例化webApplicationContext后,会把它放到servletContext的一个属性里,所以我们可以从servletContext的属性取出webApplicationContext,如果不为空,则已经实例化,接着就会抛出异常.

    if(servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {

               throw new IllegalStateException(

    "Cannot initialize context because there is already a root application context present - " +

    "check whether you have multiple ContextLoader* definitions in your web.xml!");

        }

    接着就会创建webApplicationContext

    if (this.context == null) {

        this.context = createWebApplicationContext(servletContext);

    }

    我们跟进去createWebApplicationContext这个方法看一下

    protected WebApplicationContext createWebApplicationContext(ServletContext sc)

    {

        //获取相应的class,其实就是ConfigurableWebApplicationContext.class

        Class<?> contextClass = determineContextClass(sc);

        //判断是否为 ConfigurableWebApplicationContext.class或从它继承   if(!ConfigurableWebApplicationContext.class.isAssignableFrom(cont         extClass))

    {

    //若非ConfigurableWebApplicationContext.class或非其子类则抛出异常

    throw new ApplicationContextException("Custom context class [" + contextClass.getName() +

    "] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");

        }

        //创建一个contextClass的实例对象返回

    return(ConfigurableWebApplicationContext)BeanUtils.instantiateClass(contextClass);

    }

    我们看一下determineContextClass是怎么实现的

    protected Class<?> determineContextClass(ServletContext servletContext) {

           //从servletContext读取contextClassName

           String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);

          

           if (contextClassName != null) {

               //如果从servletContext读取到的contextClassName不为空,就返回对应

               //的class类

               try {

                  //返回className对应的Class类

                  return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader());

               }//如果找不到该类名的类就抛出异常

               catch (ClassNotFoundException ex) {

                  throw new ApplicationContextException(

                         "Failed to load custom context class [" + contextClassName + "]", ex);

               }

           }

        else {//如果从servletContext读取到得contextClassName为空就取默认的className

               contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());

               try {//返回className对应的Class类

                  return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());

               }

               catch (ClassNotFoundException ex) {

                  throw new ApplicationContextException(

                         "Failed to load default context class [" + contextClassName + "]", ex);

               }

           }

        }

    至于BeanUtils.instantiateClass(contextClass);

    则是通过反射创建对应class的实体对象。

    //然后就是把context强制转换为configrableWebApplicationContext

    configrableWebApplicationContext cwac = (configrableWebApplicationContext) this.context;

    //接着就是核心方法configureAndRefreshWebApplicationContext

    configureAndRefreshWebApplicationContext(cwac, servletContext);

    //最后把它放到servletContext的属性里

    servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);

    这样webApplicationContext就算加载完成了。

    我们现在来看一下核心方法configureAndRefreshWebApplicationContext(cwac, servletContext);(省略一些不重要的代码)

    protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {

           //把servletContext放到webApplicationContext中,以后可以直接取出来用

           wac.setServletContext(sc);

           //用户自己的一些设置

           customizeContext(sc, wac);

           //进行加载

           wac.refresh();

    }

    我们进入webApplicationContext的refresh方法看一下

    public void refresh() throws BeansException, IllegalStateException {

           synchronized (this.startupShutdownMonitor) {

               // Prepare this context for refreshing.

               prepareRefresh();

               // Tell the subclass to refresh the internal bean factory.

               ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

               // Prepare the bean factory for use in this context.

               prepareBeanFactory(beanFactory);

               try {

                  // Allows post-processing of the bean factory in context subclasses.

                  postProcessBeanFactory(beanFactory);

                  // Invoke factory processors registered as beans in the context.

                  invokeBeanFactoryPostProcessors(beanFactory);

                  // Register bean processors that intercept bean creation.

                  registerBeanPostProcessors(beanFactory);

                  // Initialize message source for this context.

                  initMessageSource();

                  // Initialize event multicaster for this context.

                  initApplicationEventMulticaster();

                  // Initialize other special beans in specific context subclasses.

                  onRefresh();

                  // Check for listener beans and register them.

                  registerListeners();

                  // Instantiate all remaining (non-lazy-init) singletons.

                  finishBeanFactoryInitialization(beanFactory);

                  // Last step: publish corresponding event.

                  finishRefresh();

               }

               catch (BeansException ex) {

                  // Destroy already created singletons to avoid dangling resources.

                  destroyBeans();

                  // Reset 'active' flag.

                  cancelRefresh(ex);

                  // Propagate exception to caller.

                  throw ex;

               }

           }

        }

    这是一个同步的方法,这里最核心的方法就是obtainFreshBeanFactory方法。其他的方法都是对webApplicationContext和beanFactory做一些前后的装饰和准备。

    我们进入obtainFreshBeanFactoty方法看看

        protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {

           refreshBeanFactory();

           ConfigurableListableBeanFactory beanFactory = getBeanFactory();

           if (logger.isDebugEnabled()) {

               logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);

           }

           return beanFactory;

    }

    这里的核心方法就是refreshBeanFactory();它负责生成BeanFactory并加载bean

    protected final void refreshBeanFactory() throws BeansException {

           //判断是否已经存在beanFactory如果有则销毁。

           if (hasBeanFactory()) {

               destroyBeans();

               closeBeanFactory();

           }

           try {

               DefaultListableBeanFactory beanFactory = createBeanFactory();//创建一个beanFactory

               beanFactory.setSerializationId(getId());//给它一个标识

               customizeBeanFactory(beanFactory);//用户自己做一些设置

               //这个方法很关键,负责加载所有的bean

               loadBeanDefinitions(beanFactory);

               synchronized (this.beanFactoryMonitor) {

                  this.beanFactory = beanFactory;

               }

           }

           catch (IOException ex) {

               throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);

           }

        }

    然后我们进入loadBeanDefinitions(beanFactory);看一下

    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(this.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);

    }

    这里主要工作就是new一个XmlBeanDefinitionReader,给它设置environment,resourceLoader和entityResolver,注意一下由于webApplicationContext实现了ResouceLoader接口,所以它本身就是一个ResourceLoader.

    我们可以看到它并不自己去实现lobeanDefinitions方法,而是委托给XmlBeanDefinitionReader去实现。

        protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException {

           String[] configLocations = getConfigLocations();

           if (configLocations != null) {

               for (String configLocation : configLocations) {

                  reader.loadBeanDefinitions(configLocation);

               }

           }

        }

    这里就对configLocations进行bean的加载,调用重载的方法(spring重载的方法好多啊)

    public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {

           return loadBeanDefinitions(location, null);

    }

    public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException {

           ResourceLoader resourceLoader = getResourceLoader();

           if (resourceLoader == null) {

               throw new BeanDefinitionStoreException(

                      "Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");

           }

           if (resourceLoader instanceof ResourcePatternResolver) {

               // Resource pattern matching available.

               try {

                  Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);

                  int loadCount = loadBeanDefinitions(resources);

                  if (actualResources != null) {

                      for (Resource resource : resources) {

                         actualResources.add(resource);

                      }

                  }

                  if (logger.isDebugEnabled()) {

                      logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]");

                  }

                  return loadCount;

               }

               catch (IOException ex) {

                  throw new BeanDefinitionStoreException(

                         "Could not resolve bean definition resource pattern [" + location + "]", ex);

               }

           }

           else {

               // Can only load single resources by absolute URL.

               Resource resource = resourceLoader.getResource(location);

               int loadCount = loadBeanDefinitions(resource);

               if (actualResources != null) {

                  actualResources.add(resource);

               }

               if (logger.isDebugEnabled()) {

                  logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");

               }

               return loadCount;

           }

        }

    上面这段代码其实就是取得resourceLoader,通过location取得resouces,然后调用

    loadBeanDefinitions(resource),这里又是一个重载的方法。

    public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {

           return loadBeanDefinitions(new EncodedResource(resource));

    }

    这个方法把resource进行一下编码,再调用一下重载的方法

    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 {

               InputStream inputStream = encodedResource.getResource().getInputStream();

               try {

                  InputSource inputSource = new InputSource(inputStream);

                  if (encodedResource.getEncoding() != null) {

                      inputSource.setEncoding(encodedResource.getEncoding());

                  }

                  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();

               }

           }

        }

    通过resource取出inpustream,封装一个inputSource,调用doLoadBeanDefinitions(inputSource, encodedResource.getResource());

    protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)

               throws BeanDefinitionStoreException {

          

               int validationMode = getValidationModeForResource(resource);

               Document doc = this.documentLoader.loadDocument(

                      inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware());

               return registerBeanDefinitions(doc, resource);

    }

    这个方法是从resource中读取一个doc对象,值得注意的是,这个doc是w3c的标准。然后进行bean的注册。

    registerBeanDefinitions这个方法还没看完。 

     

  • 相关阅读:
    Bit命令
    Leetcode628. 三个数的最大乘积
    MyBatis中一级缓存和二级缓存
    Docker学习-实战MySQL数据同步
    Docker学习-容器数据卷的使用
    我的健康计划【要常看啊】
    ASCII编码对照表
    KMP算法详解
    Docker学习-制作一个自己的镜像
    Docker学习-Docker镜像的分层原理
  • 原文地址:https://www.cnblogs.com/writeLessDoMore/p/6938730.html
Copyright © 2011-2022 走看看