spring-mvc源码-bean定义加载

接着上篇,在根上下文初始化的过程中,有一步配置和启动根上下文方法:org.springframework.web.context.ContextLoader#configureAndRefreshWebApplicationContext,这里面包含了对bean的所有处理,下面我们慢慢来看。

先看下这个方法的实现:configureAndRefreshWebApplicationContext

	protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
		if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
			String idParam = sc.getInitParameter(CONTEXT_ID_PARAM);
			if (idParam != null) {
				wac.setId(idParam);
			}
			else {
				wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
						ObjectUtils.getDisplayString(sc.getContextPath()));
			}
		}

		wac.setServletContext(sc);
		//读取web.xm中配设置的contextConfigLocation参数值
		String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);
		if (configLocationParam != null) {
			wac.setConfigLocation(configLocationParam);
		}

		ConfigurableEnvironment env = wac.getEnvironment();
		if (env instanceof ConfigurableWebEnvironment) {
			((ConfigurableWebEnvironment) env).initPropertySources(sc, null);
		}

		customizeContext(sc, wac);
		//根上下文(ioc容器)的启动,bean的处理也在这里
		wac.refresh();
	}

然后进入refresh()方法:

	@Override
	public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			//准备启动上下文,设置开始时间,标记活动标志,初始化配置文件中的占位符
			prepareRefresh();

			//将 bean 定义加载到给定的 BeanFactory 中
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

			//准备 BeanFactory 以便在此上下文中使用。
			// 1. 设置 BeanFactory 的类加载器
			// 2. 添加几个 BeanPostProcessor
			// 3. 实例化几个特殊的 bean
			prepareBeanFactory(beanFactory);

			try {
				//为空实现,留给子类做扩展,不同 ApplicationContext 实现不同
				postProcessBeanFactory(beanFactory);

				// Spring 的 SPI
				// 先调用 BeanDefinitionRegistryPostProcessor 和 ImportBeanDefinitionRegistrar 的实现类
				// 再调用 BeanFactoryPostProcessor 各个实现类的 postProcessBeanFactory(factory) 方法
				// 例如:ConfigurationClassPostProcessor 会扫描 <context:component-scan/> 和 @SpringBootApplication(scanBasePackages = "") 中的Component,并且将 @Configuration 类中的 @Bean register 到 BeanFactory 中
				// 扩展例如:MyBatis MapperScannerConfigurer 和 MapperScannerRegistrar,扫描Mapper register 到 BeanFactory 中
				invokeBeanFactoryPostProcessors(beanFactory);

				// 注册 BeanPostProcessor 的实现类,不同于刚刚的 BeanFactoryPostProcessor
				// BeanPostProcessor 接口两个方法 postProcessBeforeInitialization 和 postProcessAfterInitialization 会在 Bean 初始化之前和之后调用
				// 这边 Bean 还没初始化,下面的 finishBeanFactoryInitialization 才是真正的初始化方法
				registerBeanPostProcessors(beanFactory);

				// 初始化当前 ApplicationContext 的 MessageSource,解析消息的策略接口,用于支持消息的国际化和参数化
				// Spring 两个开箱即用的实现 ResourceBundleMessageSource 和 ReloadableResourceBundleMessageSource
				initMessageSource();

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

				// 典型模板方法
				// 子类可以在实例化 bean 之前,做一些初始化工作,SpringBoot 会在这边启动 Web 服务
				onRefresh();

				// 向 initApplicationEventMulticaster() 初始化的 applicationEventMulticaster 注册事件监听器,就是实现 ApplicationListener 接口类
				// 观察者模式,例如实现了 ApplicationEvent,通过 ApplicationEventPublisher#publishEvent(),可以通知到各个 ApplicationListener#onApplicationEvent
				registerListeners();

				// 初始化所有的 singletons bean(lazy-init 的除外)
				// Spring bean 初始化核心方法
				finishBeanFactoryInitialization(beanFactory);

				// 初始化完成(ContextRefreshedEvent)事件
				finishRefresh();
			}

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

				// destroy 已经创建的 singleton 避免占用资源
				destroyBeans();

				// 重置'active'标志
				cancelRefresh(ex);

				// Propagate exception to caller.
				throw ex;
			}

			finally {
				resetCommonCaches();
			}
		}
	}

进入obtainFreshBeanFactory()方法,这个方法里面调用了refreshBeanFactory()和getBeanFactory()方法,这两个方法都是在子类AbstractRefreshableApplicationContext中实现的,主要看refreshBeanFactory()方法,如下:

	@Override
	protected final void refreshBeanFactory() throws BeansException {
		//判断当前ApplicationContext是否已经有 BeanFactory ,如果有,销毁所有 Bean,关闭 BeanFactory
		if (hasBeanFactory()) {
			destroyBeans();
			closeBeanFactory();
		}
		try {
			//创建一个可以独立使用的容器
			DefaultListableBeanFactory beanFactory = createBeanFactory();
			beanFactory.setSerializationId(getId());
			//设置 BeanFactory 的两个配置属性:是否允许 Bean 覆盖、是否允许循环引用
			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()在子类XmlWebApplicationContext实现,代码如下:

	@Override
	protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
		//创建XmlBeanDefinitionReader对象,并将beanFactory绑定,XmlBeanDefinitionReader是BeanDefinitionReader的一个实现类,负责对xml的配置文件进行读取
		XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

		// Configure the bean definition reader with this context's
		// resource loading environment.
		//设置环境,这里的环境是StandardServletEnvironment,里面主要有五项属性:servletConfigInitParams、servletContextInitParams、jndiProperties、systemProperties、systemEnvironment
		//这个StandardServletEnvironment创建比较早,在为根上下文设置configLocationParam的时候创建的
		beanDefinitionReader.setEnvironment(getEnvironment());
		beanDefinitionReader.setResourceLoader(this);
		//ResourceEntityResolver包含BeansDtdResolver和PluggableSchemaResolver,用以在classpath下搜寻schema和DTD文件
		//PluggableSchemaResolver有一个schemaMappingsLocation属性其值为写死的META-INF/spring.schemas
		beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

		//允许子类提供reader的自定义初始化,然后继续实际bean定义加载。这里是空实现
		initBeanDefinitionReader(beanDefinitionReader);
		//加载bean的定义
		loadBeanDefinitions(beanDefinitionReader);
	}

继续看loadBeanDefinitions()方法,如下:

	protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException {
		//这里获取到的配置文件,就是在web.xml里面配置的contextConfigLocation,一般是:classpath:spring-config.xml
		String[] configLocations = getConfigLocations();
		if (configLocations != null) {
			for (String configLocation : configLocations) {
				//解析配置文件,加载bean的定义
				reader.loadBeanDefinitions(configLocation);
			}
		}
	}

调用XmlBeanDefinitionReader父类AbstractBeanDefinitionReader的loadBeanDefinitions(String location)方法,这个方法直接调用的loadBeanDefinitions(String location, Set<Resource> actualResources)方法,这个解析过程都在这里,如下:

	public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException {
		//获取ResourceLoader,此处的ResourceLoader就是我们创建的XmlWebApplicationContext
		ResourceLoader resourceLoader = getResourceLoader();
		if (resourceLoader == null) {
			throw new BeanDefinitionStoreException(
					"Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
		}

		//判断XmlWebApplicationContext是否为ResourcePatternResolver,通过类继承结构可以看出,XmlWebApplicationContext是ResourcePatternResolver的子类,所以会进入第一个条件分支
		if (resourceLoader instanceof ResourcePatternResolver) {
			// Resource pattern matching available.
			try {
				//获取resources,这里的resources里面放的就是spring的xml配置文件信息
				Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
				//调用loadBeanDefinitions(Resource... resources)解析配置文件,并返回beanDefinition的数量
				int loadCount = loadBeanDefinitions(resources);
				//加载过程中已经被解析过的实际的Resource的填充集合
				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 {
			//只能通过绝对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是否是ResourcePatternResolver类型的,因为ResourceLoader只是提供了对classpath前缀的支持。而ResourcePatternResolver提供了对classpath*前缀的支持。也就是说ResourceLoader提供classpath下单资源文件的载入,而ResourcePatternResolver提供多资源文件的载入。

调用loadBeanDefinitions(Resource... resources)解析配置文件,这个方法就是循环解析所有的配置文件,具体的解析,调用子类XmlBeanDefinitionReader的loadBeanDefinitions(Resource resource)这个方法,这个方法直接调用loadBeanDefinitions(EncodedResource encodedResource)这个方法,这个方法代码如下:

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

		//检查是否重复加载xml配置
		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();
			}
		}
	}

在Spring代码中,最后真正执行操作的方法一般都是doXXXX,我们在工作中写代码也可以参考这种定义。

	protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
			throws BeanDefinitionStoreException {
		try {
			//将xml文件转成标准的Document对象,采用SAX的方式解析
			Document doc = doLoadDocument(inputSource, resource);
			//载入并注册
			return registerBeanDefinitions(doc, resource);
		}
	}

继续看org.springframework.beans.factory.xml.XmlBeanDefinitionReader#registerBeanDefinitions这个方法:

	public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
		//这里的documentReader使用的是DefaultBeanDefinitionDocumentReader
		BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
		//记录统计前的BeanDefinition数
		int countBefore = getRegistry().getBeanDefinitionCount();
		//加载并注册
		documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
		//返回本次加载的BeanDefinition数
		return getRegistry().getBeanDefinitionCount() - countBefore;
	}

继续看org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#registerBeanDefinitions这个方法:

	@Override
	public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
		this.readerContext = readerContext;
		logger.debug("Loading bean definitions");
		Element root = doc.getDocumentElement();
		//真正的注册bean定义
		doRegisterBeanDefinitions(root);
	}

接着看org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#doRegisterBeanDefinitions这个方法:

	protected void doRegisterBeanDefinitions(Element root) {
		BeanDefinitionParserDelegate parent = this.delegate;
		//创建 BeanDefinitionParserDelegate 对象,这个对象的作用是将 Document 的内容转成 BeanDefinition 实例
		this.delegate = createDelegate(getReaderContext(), root, parent);

		//验证 XML 文件的命名空间,即判断是否含有 xmlns="http://www.springframework.org/schema/beans"
		if (this.delegate.isDefaultNamespace(root)) {
			//取得 beans 标签中 profile 的属性内容,该标签主要用于环境的切换。例如开发过程中,一般存在测试环境和正式环境,两者之间可能存在不同的数据源。若想要实现环境的快速切换,就可以利用 profile 来配置。
			String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
			if (StringUtils.hasText(profileSpec)) {
				String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
						profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
				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;
				}
			}
		}

		//前置处理(空实现,供子类实现,方便扩展)
		preProcessXml(root);
		//开始解析 Bean 定义
		parseBeanDefinitions(root, this.delegate);
		//后置处理(空实现,供子类实现,方便扩展)
		postProcessXml(root);

		this.delegate = parent;
	}

继续看org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#parseBeanDefinitions这个方法:

	protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
		//验证 XML 文件的命名空间,即判断是否含有 xmlns="http://www.springframework.org/schema/beans"
		if (delegate.isDefaultNamespace(root)) {
			//取得 <beans> 的所有子节点
			NodeList nl = root.getChildNodes();
			//遍历 <beans> 的子节点
			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)) {
						//解析 <beans> 子节点的内容
						parseDefaultElement(ele, delegate);
					}
					else {
						//非xmlns="http://www.springframework.org/schema/beans"这个命名空间的标签解析,例如:<context:annotation-config/>
						delegate.parseCustomElement(ele);
					}
				}
			}
		}
		else {
			//非xmlns="http://www.springframework.org/schema/beans"这个命名空间的标签解析,例如:<context:annotation-config/>
			delegate.parseCustomElement(root);
		}
	}

这里可以看到,bean的定义解析分成了两个分支,一个是bean标签的解析,一个是非bean标签及自定义标签的解析,这块内容也很多,下篇继续。

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring MVC的源码包括多个组件和类。其中,Tomcat在启动时会通知Spring初始化容器,加载bean的定义信息并初始化所有单例bean。然后,Spring MVC会遍历容器中的bean,获取每个controller中方法访问的URL,并将URL和Controller保存到一个Map中。这一过程是由HandlerMapping组件完成的,它是Spring MVC中负责URL到Controller映射的组件。此外,在Spring MVC的源码中还有一个抽象类FrameworkServlet,它重写了初始化方法initServletBean(),可以在控制台或日志中打印初始化Servlet的名称以及初始化所需的时间。 以上是关于Spring MVC源码的一些重要信息,这些组件和类协同工作,实现了Spring MVC框架的核心功能。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [Spring MVC源码分析](https://blog.csdn.net/qq_38826019/article/details/117877511)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *3* [SpringMVC源码解析](https://blog.csdn.net/qq_35512802/article/details/120659719)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值