ApplicationContext refresh

刷新上下文

1: refresh()

refresh方法是Spring核心的方法,Spring容器的初始化实在该方法中完成, 这里暂时只介绍prepareRefreshobtainFreshBeanFactory

@Override
	public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
            //为刷新准备新的上下文环境,设置其启动日期和活动标志以及执行一些属性的初始化;
			prepareRefresh();

            //获取BeanFactory;默认实现是DefaultListableBeanFactory,在创建容器的时候创建的,
            // 配置文件就会解析成一个个 Bean 定义,注册到 BeanFactory 中,当然,这里说的 Bean 还没有初始化,只是配置信息都提取出来了,
	        // 注册也只是将这些信息都保存到了注册中心(其实就是map beanName:beanDefinition形势储存)
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

            //BeanFactory的预准备工作(BeanFactory进行一些设置,设置 BeanFactory 的类加载器, 比如context的类加载器,
            // 添加几个 BeanPostProcessor, 如BeanPostProcessor自动装配等)
			prepareBeanFactory(beanFactory);

			try {
				

	    	     // 这里是提供给子类的扩展点,到这里的时候,所有的 Bean 都加载、注册完成了,但是都还没有初始化
    	    	 // 如果实现Bean BeanFactoryPostProcessor此接口,
    	     	 // 那么在容器初始化以后,Spring 会负责调用里面的 postProcessBeanFactory 方法。
				postProcessBeanFactory(beanFactory);

                // 调用 BeanFactoryPostProcessor 各个实现类的 postProcessBeanFactory(factory) 方法
				invokeBeanFactoryPostProcessors(beanFactory);

			 	// 注册 BeanPostProcessor 的实现类
         		// 此接口两个方法: postProcessBeforeInitialization 和 postProcessAfterInitialization
         		// 两个方法分别在 Bean 初始化之前和初始化之后得到执行。到这里 Bean 还没初始化
				registerBeanPostProcessors(beanFactory);

				//初始化MessageSource组件(做国际化功能;消息绑定,消息解析);
				initMessageSource();

				//初始化当前 ApplicationContext 的事件广播器
				initApplicationEventMulticaster();

				//典型的模板方法(钩子方法),子类重写这个方法,在容器刷新的时候可以自定义逻辑;
                // 具体的子类可以在这里初始化一些特殊的 Bean(在初始化 singleton beans 之前)
				onRefresh();

				//注册应用的监听器。就是注册实现了ApplicationListener接口的监听器bean,这些监听器是注册到ApplicationEventMulticaster中的
				registerListeners();

				 //初始化所有剩下的非懒加载的单例bean
				finishBeanFactoryInitialization(beanFactory);

				 //完成context的刷新。主要是调用LifecycleProcessor的onRefresh()方法,并且发布事件(ContextRefreshedEvent)
				finishRefresh();
			}

			catch (BeansException ex) {
				if (logger.isWarnEnabled()) {
					logger.warn("Exception encountered during context initialization - " +
							"cancelling refresh attempt: " + ex);
				}

				// 销毁已经初始化的 singleton 的 Beans,以免有些 bean 会一直占用资源
				destroyBeans();

				// Reset 'active' flag.
				cancelRefresh(ex);

				// Propagate exception to caller.
				throw ex;
			}

			finally {
				// Reset common introspection caches in Spring's core, since we
				// might not ever need metadata for singleton beans anymore...
				resetCommonCaches();
			}
		}
	}
1.1: prepareRefresh()

表示在真正做refresh操作之前需要准备做的事情:

  • 设置Spring容器的启动时间,
  • 开启活跃状态,撤销关闭状态,。
  • 初始化context environment(上下文环境)中的占位符属性来源。
  • 验证环境信息里一些必须存在的属性
	protected void prepareRefresh() {
        // 设置容器启动的时间
		this.startupDate = System.currentTimeMillis();
        // 容器的关闭标志位
		this.closed.set(false);
        // 容器的激活标志位
		this.active.set(true);

		if (logger.isInfoEnabled()) {
			logger.info("Refreshing " + this);
		}

		// 子类扩展,初始化属性资源, 默认是个空方法, 可以重写此方法进行一些初始化操作
		initPropertySources();

		// 创建并获取环境对象,验证需要的属性文件是否都已经放入环境中
		getEnvironment().validateRequiredProperties();

		// 创建刷新前的监听事件集合
		this.earlyApplicationEvents = new LinkedHashSet<>();
	}

initPropertySources 扩展

该方法可以在子类中扩展, 因此需要自定义ApplicationContext 去继承AbstractApplicationContextspring默认的容器XmlWebApplicationContext 是该类的子类, 我们继承XmlWebApplicationContext即可, 然后再web.xml指定容器,即可实现扩展

public class MyXmlWebApplicationContext extends XmlWebApplicationContext {
    @Override
    public void initPropertySources(){
        System.out.println("扩展initPropertySource");
    }
}
  <context-param>
    <param-name>contextClass</param-name>
    <param-value>com.zhexinit.MyXmlWebApplicationContext</param-value>
  </context-param>
1.2:obtainFreshBeanFactory

这是用于获得一个新的 BeanFactory

该方法会解析所有 Spring 配置文件,将所有 Spring 配置文件中的 bean 定义封装成 BeanDefinition,然后加载到 BeanFactory 中。

如果解析到<context:component-scan base-package="com.zhexinit" /> 注解时,会扫描 base-package 指定的目录,将该目录下使用@Controller@Service@Component@Repository bean 定义也同样封装成 BeanDefinition,加载到 BeanFactory 中。
起始实现的方式主要指的是实现以下3个缓存:

beanDefinitionNames缓存:所有被加载到 BeanFactory 中的 beanbeanName 集合。
beanDefinitionMap缓存:所有被加载到 BeanFactory 中的bean beanNameBeanDefinition映射。
aliasMap缓存:所有被加载到 BeanFactory 中的 beanbeanName 和别名映射。

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
    // 此实现执行此上下文的底层bean工厂的实际刷新,销毁之前的bean工厂,并为上下文生命周期的下一阶段初始化一个新的bean工厂。
    refreshBeanFactory();
    return getBeanFactory();
}

protected final void refreshBeanFactory() throws BeansException {
	    // 判断BeanFactory是否存在,存在则销毁所有的Beans, 并且关闭BeanFacotry, 避免重复加载BeanFactory,
        // 这里的操作调用了DefaultSingletonBeanRegistry 的destroySingletons()方法, 
    	// 方法内使用对象锁进行同步主要是从单例对象3个的缓存map中删除这个实例
		if (hasBeanFactory()) {
			destroyBeans();
			closeBeanFactory();
		}
		try {
            //创建 BeanFactory,内部会检测父工厂,默认父工厂为null
			DefaultListableBeanFactory beanFactory = createBeanFactory();
            //设置序列化id,以 webApplicationContext 的id相同
            // 例如: org.springframework.web.context.WebApplicationContext:/SpringOrignal_war
            // 用于 BeanFactory 的序列化,我想不部分人应该都用不到
			beanFactory.setSerializationId(getId());
            //更新 allowBeanDefinitionOverriding 和 allowCircularReferences 的值,
            // allowBeanDefinitionOverriding 这个值是是否允许重名的bean可以被覆盖, 
            // 在DefaultListableBeanFactory 中这个值默认是true,allowCircularReferences 是循环依赖, 也默认为true
			customizeBeanFactory(beanFactory);
            //加载 Bean 到 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);
		}
	}


1.2.1:createBeanFactory()
protected DefaultListableBeanFactory createBeanFactory() {
    return new DefaultListableBeanFactory(getInternalParentBeanFactory());
}
1.2.1.1:DefaultListableBeanFactory()

org.springframework.beans.factory.support.DefaultListableBeanFactory
DefaultListableBeanFactorySpring 中的一个核心类。
DefaultListableBeanFactorySpring中默认的ListableBeanFactoryBeanDefinitionRegistry的实现类,功能点是比较多的
比如可以在调用相关的SpringBean之前,通过Bean定义文件对Bean进行注册。

public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
		implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {

	@Nullable
	private static Class<?> javaxInjectProviderClass;

	static {
		try {
			javaxInjectProviderClass =
					ClassUtils.forName("javax.inject.Provider", DefaultListableBeanFactory.class.getClassLoader());
		}
		catch (ClassNotFoundException ex) {
			// JSR-330 API not available - Provider interface simply not supported then.
			javaxInjectProviderClass = null;
		}
	}


    // 储存工厂实例 key:value 为 序列化ID:DefaultListableBeanFactory实例对象
	private static final Map<String, Reference<DefaultListableBeanFactory>> serializableFactories =
			new ConcurrentHashMap<>(8);

	/** 该工厂的可选id, 用于序列化 */
	@Nullable
	private String serializationId;

	/** 是否允许同名 bean 定义的重新注册,即同名bean后面的覆盖之前的,默认是true */
	private boolean allowBeanDefinitionOverriding = true;

	/** 是否允许立即加载 包括对于 lazy-init beans,默认是允许 */
	private boolean allowEagerClassLoading = true;

	/** 依赖项列表和数组的可选比较器 */
	@Nullable
	private Comparator<Object> dependencyComparator;

	/**用于检测 Bean 是否有 自动注入请求 */
	private AutowireCandidateResolver autowireCandidateResolver = new SimpleAutowireCandidateResolver();

	/** 保存 依赖类型 和 依赖的对象 的map */
	private final Map<Class<?>, Object> resolvableDependencies = new ConcurrentHashMap<>(16);

	/** map,key:value 为 bean的名字:定义bean的相关对象 */
	private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);

	/** map key:value  为 依赖类型:单例和非单例 bean的名字 */
	private final Map<Class<?>, String[]> allBeanNamesByType = new ConcurrentHashMap<>(64);

	/** map,key:依赖类型,value:单例bean的名字. */
	private final Map<Class<?>, String[]> singletonBeanNamesByType = new ConcurrentHashMap<>(64);

	/**list:bean定义的名字,按照注册顺序保存  */
	private volatile List<String> beanDefinitionNames = new ArrayList<>(256);

	/** list:手动注册单例bean的名字,按照注册顺序 */
	private volatile Set<String> manualSingletonNames = new LinkedHashSet<>(16);

	/** 为了冻结配置 提供的 bean名字的缓存数组 */
	@Nullable
	private volatile String[] frozenBeanDefinitionNames;

	/** 是否允许bean定义的元数据被缓存,以用于所有的bean,默认是否 */
	private volatile boolean configurationFrozen = false;

1.2.1.2:AbstractAutowireCapableBeanFactory()

DefaultListableBeanFactory 的父类。

public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory
		implements AutowireCapableBeanFactory {

	/** 创建 bean实例的策略默认使用 Cglib 动态代理,通过动态生成子类的方式对方法进行注入 */
	private InstantiationStrategy instantiationStrategy = new CglibSubclassingInstantiationStrategy();

	/** 方法参数名称的解析策略 */
	@Nullable
	private ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();

	/**  是否允许循环依赖的 beans,默认是允许的 */
	private boolean allowCircularReferences = true;

	/**
	 * 是否在循环依赖的时候尝试将 bean 的原始对象注入到参数中,即使这个bean是被包装的(比如AOP)
	 */
	private boolean allowRawInjectionDespiteWrapping = false;

	/**
	 * 设置需要忽略依赖检测和注入的类型,比如 String 
	 */
	private final Set<Class<?>> ignoredDependencyTypes = new HashSet<>();

	/**
	 * 设置需要忽略依赖和注入的接口,默认只有 BeanFactory interface,但是在 DefaultListableBeanFactory 中其实增加了 BeanNameAware、	BeanFactoryAware和BeanClassLoaderAware 三个接口要被忽略
	 */
	private final Set<Class<?>> ignoredDependencyInterfaces = new HashSet<>();

    /*
	 * 当前创建的bean的名称,用于回调触发的getBean 等调用的隐式依赖项注册。
	 */
	private final NamedThreadLocal<String> currentlyCreatedBean = new NamedThreadLocal<>("Currently created bean");

	/** 没有完成实例化的 FactoryBean 缓存map,key:FactoryBean 名称 ,value:BeanWrapper */
	private final ConcurrentMap<String, BeanWrapper> factoryBeanInstanceCache = new ConcurrentHashMap<>();

	/** 每个工厂类的候选工厂方法缓存 */
	private final ConcurrentMap<Class<?>, Method[]> factoryMethodCandidateCache = new ConcurrentHashMap<>();

	/** 过滤了的属性描述器 缓存map, key:bean Class,value:PropertyDescriptor 数据 */
	private final ConcurrentMap<Class<?>, PropertyDescriptor[]> filteredPropertyDescriptorsCache =
			new ConcurrentHashMap<>();
1.2.2:loadBeanDefinitions

用于加载定义 bean 的配置文件,一般我们会定义一个或者多个 applicationContext.xml

@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
    // 为 指定的beanFactory 创建一个 XmlBeanDefinitionReader 对象
    XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

    // 使用上下文资源环境加载 bean定义的 reader
    // resource loading environment.
    beanDefinitionReader.setEnvironment(this.getEnvironment());
    // resourceLoader赋值为XmlWebApplicationContext
    beanDefinitionReader.setResourceLoader(this);
    beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

    // 允许一个子列提供自定义的 reader对象的实现,然后再开始具体bean 定义加载过程
    initBeanDefinitionReader(beanDefinitionReader);
    // 加载
    loadBeanDefinitions(beanDefinitionReader);
1.2.2.1:loadBeanDefinitions

使用 XmlBeanDefinitionReader 加载 bean定义文件。

protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
	String[] configLocations = getConfigLocations();
    if (configLocations != null) {
        for (String configLocation : configLocations) {
            // 根据配置文件路径加载 bean 定义
            reader.loadBeanDefinitions(configLocation);
        }
    }
}
1.2.2.1.1:getConfigLocations
public String[] getConfigLocations() {
    return super.getConfigLocations();
}

@Nullable
protected String[] getConfigLocations() {
    return (this.configLocations != null ? this.configLocations : getDefaultConfigLocations());
}


@Override
protected String[] getDefaultConfigLocations() {
    if (getNamespace() != null) {
        return new String[] {DEFAULT_CONFIG_LOCATION_PREFIX + getNamespace() + DEFAULT_CONFIG_LOCATION_SUFFIX};
    }
    else {
        return new String[] {DEFAULT_CONFIG_LOCATION};
    }
}

获取文件的配置路径, 如果configLocations是存在的, 那么直接返回configLocations 的值,即 web.xml文件中获取<param-name>contextConfigLocation</param-name> 属性对应的参数值, 如果该值是不存在的, 那么调用getDefaultConfigLocations方法拿到Spring默认的路径/WEB-INF/applicationContext.xml

1.2.2.1.2:loadBeanDefinitions
@Override
public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {
    return loadBeanDefinitions(location, null);
}

public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
	    // 获取 resourceLoader
		ResourceLoader resourceLoader = getResourceLoader();
		if (resourceLoader == null) {
			throw new BeanDefinitionStoreException(
					"Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
		}
		// 判断 resourceLoader 是否为 ResourcePatternResolver 的实例, 
		if (resourceLoader instanceof ResourcePatternResolver) {
			// Resource pattern matching available.
			try {
                // 根据路径拿到该路径下所有符合的配置文件,并封装成Resource
				Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
                // 根据Resource,加载Bean的定义
				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.
            // 只能通过绝对URL加载单个资源
			Resource resource = resourceLoader.getResource(location);
            // 根据Resource,加载Bean的定义
			int loadCount = loadBeanDefinitions(resource);
			if (actualResources != null) {
				actualResources.add(resource);
			}
			if (logger.isDebugEnabled()) {
				logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");
			}
			return loadCount;
		}
	}

根据上述源码可以了解到, 是将XmlWebApplicationContext 赋值给resourceLoader,并且根据XmlWebApplicationContext 的继承关系图可以了解到XmlWebApplicationContext实现了ResourcePatternResolver接口, 然后根据获取到的配置文件的路径获取到配置文件, 且封装成Resource, 然后根据Resource加载Bean, 见1.2.2.1.2.1:loadBeanDefinitions

1.2.2.1.2.1:loadBeanDefinitions
@Override
public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
    Assert.notNull(resources, "Resource array must not be null");
    int counter = 0;
    // 1.遍历所有的Resource
    for (Resource resource : resources) {
        // 2.根据Resource加载bean的定义,XmlBeanDefinitionReader实现
        counter += loadBeanDefinitions(resource);
    }
    return counter;
}

利用XmlBeanDefinitionReader实现加载Bean1.2.2.1.2.1.1:loadBeanDefinitions

1.2.2.1.2.1.1:loadBeanDefinitions
@Override
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
    // 加载 bean 定义
    return loadBeanDefinitions(new EncodedResource(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());
    }
 
    // 1.当前正在加载的EncodedResource
    Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
    if (currentResources == null) {
        currentResources = new HashSet<EncodedResource>(4);
        this.resourcesCurrentlyBeingLoaded.set(currentResources);
    }
    // 2.将当前encodedResource添加到currentResources
    if (!currentResources.add(encodedResource)) {
        // 如果添加失败,代表当前的encodedResource已经存在,则表示出现了循环加载
        throw new BeanDefinitionStoreException(
                "Detected cyclic loading of " + encodedResource + " - check your import definitions!");
    }
    try {
        // 3.拿到Resource的inputStream
        InputStream inputStream = encodedResource.getResource().getInputStream();
        try {
            // 4.将inputStream封装成InputSource
            InputSource inputSource = new InputSource(inputStream);
            if (encodedResource.getEncoding() != null) {
                inputSource.setEncoding(encodedResource.getEncoding());
            }
            // 5.加载 bean 定义(方法以do开头,真正处理的方法)
            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();
        }
    }
}

加载 bean 定义,真正处理的方法, 详情见

1.2.2.1.2.1.1.1:doLoadBeanDefinitions
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
        throws BeanDefinitionStoreException {
    try {
        // 1.根据inputSource和resource加载XML文件,并封装成Document
        Document doc = doLoadDocument(inputSource, resource);
        // 2.根据返回的Document,对配置文件进行解析注册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);
    }
}
1.2.2.1.2.1.1.1.1.1:doLoadDocument
protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
    // 1.getValidationModeForResource(resource): 获取XML配置文件的验证模式
    // 2.documentLoader.loadDocument: 加载XML文件,并得到对应的 Document
    return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
            getValidationModeForResource(resource), isNamespaceAware());
}
 
protected int getValidationModeForResource(Resource resource) {
    int validationModeToUse = getValidationMode();
    // 1.1 如果手动指定了XML文件的验证模式则使用指定的验证模式
    if (validationModeToUse != VALIDATION_AUTO) {
        return validationModeToUse;
    }
    // 1.2 如果未指定则使用自动检测
    int detectedMode = detectValidationMode(resource);
    // 1.3 如果检测出的验证模式不为 VALIDATION_AUTO, 则返回检测出来的验证模式
    if (detectedMode != VALIDATION_AUTO) {
        return detectedMode;
    }
    // Hmm, we didn't get a clear indication... Let's assume XSD,
    // since apparently no DTD declaration has been found up until
    // detection stopped (before finding the document's root tag).
    // 1.4 如果最终没找到验证模式,则使用 XSD
    return VALIDATION_XSD;
}
 
protected int detectValidationMode(Resource resource) {
    // 1.2.1 校验resource代表的资源是否已经被打开
    if (resource.isOpen()) {
        throw new BeanDefinitionStoreException(
                "Passed-in Resource [" + resource + "] contains an open stream: " +
                        "cannot determine validation mode automatically. Either pass in a Resource " +
                        "that is able to create fresh streams, or explicitly specify the validationMode " +
                        "on your XmlBeanDefinitionReader instance.");
    }
 
    InputStream inputStream;
    try {
        // 1.2.2 校验resource是否可以打开InputStream
        inputStream = resource.getInputStream();
    } catch (IOException ex) {
        throw new BeanDefinitionStoreException(
                "Unable to determine validation mode for [" + resource + "]: cannot open InputStream. " +
                        "Did you attempt to load directly from a SAX InputSource without specifying the " +
                        "validationMode on your XmlBeanDefinitionReader instance?", ex);
    }
 
    try {
        // 1.2.3 根据inputStream检测验证模式
        return this.validationModeDetector.detectValidationMode(inputStream);
    } catch (IOException ex) {
        throw new BeanDefinitionStoreException("Unable to determine validation mode for [" +
                resource + "]: an error occurred whilst reading from the InputStream.", ex);
    }
}
 
public int detectValidationMode(InputStream inputStream) throws IOException {
    // Peek into the file to look for DOCTYPE.
    BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
    try {
        boolean isDtdValidated = false;
        String content;
        // 1.2.3.1 按行遍历xml配置文件,获取xml文件的验证模式
        while ((content = reader.readLine()) != null) {
            content = consumeCommentTokens(content);
            // 如果读取的行是空或者注释则略过
            if (this.inComment || !StringUtils.hasText(content)) {
                continue;
            }
            // 内容包含"DOCTYPE"则为DTD,否则为XSD
            if (hasDoctype(content)) {
                isDtdValidated = true;
                break;
            }
            // 如果content带有 '<' 开始符号,则结束遍历。因为验证模式一定会在开始符号之前,所以到此可以认为没有验证模式
            if (hasOpeningTag(content)) {
                // End of meaningful data...
                break;
            }
        }
        // 1.2.3.2 根据遍历结果返回验证模式是 DTD 还是 XSD
        return (isDtdValidated ? VALIDATION_DTD : VALIDATION_XSD);
    } catch (CharConversionException ex) {
        // Choked on some character encoding...
        // Leave the decision up to the caller.
        return VALIDATION_AUTO;
    } finally {
        reader.close();
    }
}
 
// DefaultDocumentLoader.java
@Override
public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
        ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {
    // 2.1 创建DocumentBuilderFactory
    DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
    if (logger.isDebugEnabled()) {
        logger.debug("Using JAXP provider [" + factory.getClass().getName() + "]");
    }
    // 2.2 通过DocumentBuilderFactory创建DocumentBuilder
    DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
    // 2.3 使用DocumentBuilder解析inputSource返回Document对象
    return builder.parse(inputSource);
}

xml文件中设置验证模式

 <!-- dtd 验证模式 -->>
<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" > 

 <!-- xsd 验证模式 -->>
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
1.2.2.1.2.1.1.1.2:registerBeanDefinitions
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
    // 1.利用DefaultBeanDefinitionDocumentReader生成实例, 并向上转成BeanDefinitionDocumentReader
    BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
    // 2.记录统计前BeanDefinition的加载个数
    int countBefore = getRegistry().getBeanDefinitionCount();
    /* 3.createReaderContext:根据resource创建一个XmlReaderContext, 用于存放解析时会用到的一些上下文信息, 其中有一个namespaceHandlerResolver
    属性值是DefaultNamespaceHandlerResolver对象, 该对象中存在handlerMappings属性, 该属性用于存放命名空间和改命名空间handler类, 该类的值默认存在		于"“META-INF/spring.handlers” 文件下"
    */
    // 4.registerBeanDefinitions:加载及注册Bean定义
    documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
    // 5.返回本次加载的BeanDefinition个数
    return getRegistry().getBeanDefinitionCount() - countBefore;
}

registerBeanDefinitions方法见1.2.2.1.2.1.1.1.2.1:registerBeanDefinitions

1.2.2.1.2.1.1.1.2.1:registerBeanDefinitions
@Override
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
    this.readerContext = readerContext;
    logger.debug("Loading bean definitions");
    // 1.拿到文档的子节点,对于Spring的配置文件来说,理论上应该都是<beans>
    Element root = doc.getDocumentElement();
    // 2.通过拿到的节点,注册 Bean 定义
    doRegisterBeanDefinitions(root);
}
1.2.2.1.2.1.1.1.2.1.1:doRegisterBeanDefinitions
protected void doRegisterBeanDefinitions(Element root) {
    // Any nested <beans> elements will cause recursion in this method. In
    // order to propagate and preserve <beans> default-* attributes correctly,
    // keep track of the current (parent) delegate, which may be null. Create
    // the new (child) delegate with a reference to the parent for fallback purposes,
    // then ultimately reset this.delegate back to its original (parent) reference.
    // this behavior emulates a stack of delegates without actually necessitating one.
    BeanDefinitionParserDelegate parent = this.delegate;
    // 构建BeanDefinitionParserDelegate
    this.delegate = createDelegate(getReaderContext(), root, parent);
 
    // 1.校验root节点的命名空间是否为默认的命名空间http://www.springframework.org/schema/beans
    if (this.delegate.isDefaultNamespace(root)) {
        // 2.处理profile属性
        String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
        if (StringUtils.hasText(profileSpec)) {
            String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
                    profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
            // 校验当前节点的 profile 是否符合当前环境定义的, 如果不是则直接跳过, 不解析该节点下的内容
            if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
                if (logger.isInfoEnabled()) {
                    logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec +
                            "] not matching: " + getReaderContext().getResource());
                }
                return;
            }
        }
    }
    // 3.解析前处理, 留给子类实现
    preProcessXml(root);
    // 4.解析并注册bean定义
    parseBeanDefinitions(root, this.delegate);
    // 5.解析后处理, 留给子类实现
    postProcessXml(root);
 
    this.delegate = parent;
}

profile 校验, 我们可以在配置文件中写上多套配置适用于开发, 测试, 生产等环境, 具体使用哪个环境在web.xml中通过参数spring.profiles.active进行配置

spring-context.xml

web.xml

1.2.2.1.2.1.1.1.2.1.1:parseBeanDefinitions

下述代码是解析bean的核心部分, 这里会遍历root节点(一般都是是)下的所有子节点, 然后对子节点进行处理

如果节点的命名空间是spring的默认命名空间http://www.springframework.org/schema/beans, 比如<bean id="test" class="" />那么走默认方法parseDefaultElement解析,否则走delegate.parseCustomElement方法解析, 其他自定义的命名空间<xmlns:xsi=...>,<xmlns:aop=...> 这样的

parseDefaultElement,parseCustomElement就是解析封装bean的核心方法

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
    // 1.默认命名空间的处理
    if (delegate.isDefaultNamespace(root)) {
        NodeList nl = root.getChildNodes();
        // 遍历root的子节点列表
        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)) {
                    // 1.1 默认命名空间节点的处理,例如: <bean id="test" class="" />
                    parseDefaultElement(ele, delegate);
                }
                else {
                    // 1.2 自定义命名空间节点的处理,例如:<context:component-scan/>、<aop:aspectj-autoproxy/>
                    delegate.parseCustomElement(ele);
                }
            }
        }
    } else {
        // 2.自定义命名空间的处理
        delegate.parseCustomElement(root);
    }
}

posted @   苜蓿椒盐  阅读(53)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 上周热点回顾(3.3-3.9)
· AI 智能体引爆开源社区「GitHub 热点速览」
点击右上角即可分享
微信分享提示