spring源码---IOC:解析xml

     不管是xml方式启动,还是annotation方式启动,它都会来到refresh()方法,但是这个里面使用了很多委派模式,在xml中ApplicationContext使用了五层结构,而annotation使用了三层结构,他们委派模式的调用,调用的是各自实现的方法,不相同!(因为他们是从底层调用的,根据唯一父类的特性,它只能识别到它所在线路中的方法!)

接着上一篇,写道refresh(),在这个方法里面有14个方法,加载流程,我们一个一个的进去看一看到底发生了什么。

 ==============    XML版本  ========================

     在早期,xml方式是相当重要的,所以它给的xml配置内容,标签也是极为丰富,所以它的重点就放在了如何正确从xml中解析出所需要的信息,而后来注解方式的流行,到现在的springboot,可以说,这个复杂的xml解析,可以放弃了。

一,prepareRefresh()刷新前的预处理

  1)、initPropertySources()初始化一些属性设置;子类自定义个性化的属性设置方法;

  2)、getEnvironment().validateRequiredProperties();检验属性的合法等

  3)、earlyApplicationEvents= new LinkedHashSet();保存容器中的一些早期的事件;
参考:https://www.cnblogs.com/feng-jq/p/10282179.html

protected void prepareRefresh() {
		//切换到活动状态。
		this.startupDate = System.currentTimeMillis();
		this.closed.set(false);
		this.active.set(true);
		//在上下文环境中初始化任何占位符属性源。
		initPropertySources();         //【1】
		//验证标记为所需的所有属性都是可解析的:
		//请参阅配置属性解释器设置所需属性
		getEnvironment().validateRequiredProperties();//【2】
		if (this.earlyApplicationListeners == null) {
//容器初始化,size=0  【3】
			this.earlyApplicationListeners = new LinkedHashSet<>(this.applicationListeners);
		}
		else {
			this.applicationListeners.clear();
			this.applicationListeners.addAll(this.earlyApplicationListeners);
		}
//容器初始化 size=0
		//一旦有可利用的广播,允许早期的applicationEvents集合被发布
		this.earlyApplicationEvents = new LinkedHashSet<>();
	}

 这个存在于AbstractRefreshableWebApplicationtext中,我们使用xml启动,不会用到该方法。!

protected void initPropertySources() {
		ConfigurableEnvironment env = getEnvironment();
		if (env instanceof ConfigurableWebEnvironment) {
			//初始化属性 源
			((ConfigurableWebEnvironment) env).initPropertySources(this.servletContext, this.servletConfig);
		}
	}
//用真正的servlet context/config 属性源来替换作为占位符存在的 stub 属性源
	public void initPropertySources(@Nullable ServletContext servletContext, @Nullable ServletConfig servletConfig) {
				WebApplicationContextUtils.initServletPropertySources(getPropertySources(), servletContext, servletConfig);
	}

可以再深入这个方法看一下,上面那个参考连接讲的蛮清楚的。这里就不叙述了,总的来说就是初始化占位符属性源,用真正的属性源来替换作为占位符存在的stub属性源。

二,obtainFreshBeanFactory()获取BeanFactory

 1)、refreshBeanFactory();刷新【创建】BeanFactory;

  创建了一个this.beanFactory = new DefaultListableBeanFactory();

  设置id;

 2)、getBeanFactory();返回刚才GenericApplicationContext创建的BeanFactory对象;

 3)、将创建的BeanFactory【DefaultListableBeanFactory】返回;

可以参考:https://www.jianshu.com/p/144af98965d9

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
   //第二层类 AbstractApplicationContext作为第一层
   refreshBeanFactory();
   //返回DefaultListableBeanFactory 也由第二层实现,获得上 方法初始化的结果。
   ConfigurableListableBeanFactory beanFactory = getBeanFactory();
   return beanFactory;
}

返回的是ConfigurableListableBeanFactory接口类是BeanFactory的子类,它的唯一实现就是DefaultListableBeanFactory,所以返回的就是这个。

DefaultListableBeanFactory beanFactory,是第二层的成员,第一个初始化,第二个返回值。
第二层:AbstractRefreshApplicationContext类实现refreshBeanFactory()方法

protected final void refreshBeanFactory() throws BeansException {
		...
    		//创建IOC容器。
			DefaultListableBeanFactory beanFactory = createBeanFactory();
			beanFactory.setSerializationId(getId());
			//对容器进行定制化,
			customizeBeanFactory(beanFactory);
			//容器已经装备好了,要往里面 放东西了,放什么呢? beanDefinition。
			//调用载入bean定义的方法,【委派模式】,调用子类的实现。beanDefinition入场。
			loadBeanDefinitions(beanFactory);
			synchronized (this.beanFactoryMonitor) {
				this.beanFactory = beanFactory;
			}
	
	}
// createBeanFactory()
protected DefaultListableBeanFactory createBeanFactory() {
   return new DefaultListableBeanFactory(getInternalParentBeanFactory());
}

重点是 loadBeanDefinitions()。深入看一下,上面的方法都是对容器进行一些属性设置。

它也委派模式,实现是在第四层:AbstractXmlApplicationContext
loadBeanDefinitions 第一次

//创建一个Bean读取器,然后给这个读取器,设置了很多属性。
	protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
		//创建一个Bean读取器 reader将beanFactory封装了
		XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
		
		beanDefinitionReader.setEnvironment(this.getEnvironment());
		beanDefinitionReader.setResourceLoader(this);
		beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
		
//当Bean读取器 读取bean定义的xml资源时,启动xml的效验机制
		initBeanDefinitionReader(beanDefinitionReader);
//bean读取器真正实现加载的方法。---------【重点】
		loadBeanDefinitions(beanDefinitionReader);
	}

  new出了一个Reader,而且是把beanFactory作为参数传进去的,很明显reader把beanfactory封装了,然后传入下一个方法loadBeanDefinitions().这里还给reader配置了一些属性。
 【插入】Reader也很重要!


Reader构造方法,注意这里的BeanFactory变成了接口,改名未registry,通过抽象父类保存

	public XmlBeanDefinitionReader(BeanDefinitionRegistry registry) {
		super(registry);
	}

看一下Reader具有的成员:
在这里插入图片描述

注意:在XmlBeanDefinitionReader中,除了大一堆的成员变量的get/set方法外,就是loadBeanDefinitions方法

【插入完毕】

loadBeanDefinitions 第二次
 同样第四层AbstractXmlApplicationContext的loadBeanDefintions(),参数变成reader

protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
   Resource[] configResources = getConfigResources();//调用第五层方法
   if (configResources != null) {  //为空
      reader.loadBeanDefinitions(configResources);
   }
   String[] configLocations = getConfigLocations(); //这个有内容,xml路径组
   if (configLocations != null) {
      reader.loadBeanDefinitions(configLocations); //【入】
   }
}

  首先是获取Resource,但是第一次加载还没有,还处于location时代,所以再一次loadBeanDefinitioins(configLocations) 这是第三次load,参数是locations.
loadBeanDefinitions 第三次  位于Reader的父类:AbstractBeanDefinitionReader

public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException {
		int counter = 0;
		for (String location : locations) {
			counter += loadBeanDefinitions(location);
		}
		return counter;
	}

loadBeanDefinitions 第四次,还在AbstractBeanDefinitionReader中

	public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {
		return loadBeanDefinitions(location, null);
	}

loadBeanDefinitions 第五次,还在AbstractBeanDefinitionReader中, 感觉马上接近真相了

public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
		//该对象,是本类保存的字段,同时xml方式启动就是 第五层(记得其中一个顶层接口就是resourceLoader)
		ResourceLoader resourceLoader = getResourceLoader();
		if (resourceLoader instanceof ResourcePatternResolver) {
			// Resource pattern matching available.
			try {
//【入】将location解析成为Resource,一个location可能对应对个xml,而传入还可以传入多个location。
				Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
				//这里就 递归回到了之前,调用这个为空时候的状况,现在肯定不是空的了!
				int loadCount = loadBeanDefinitions(resources);
				...
				return loadCount;
			}
		}
		else {
			// Can only load single resources by absolute URL.
			Resource resource = resourceLoader.getResource(location);
			int loadCount = loadBeanDefinitions(resource);
			...
			return loadCount;
		}
	}

   上面方法,两个重要内容:

1.将location解析成为resource

2.调用loadBeanDefinitions(resources)

这里需要注意,走的是if()块,它的第一个方法就将location解析称为Resource.
{location-》xml找到resource-》->document-》loader-》resolver.}
判断location是哪种形式,url还是classPath 还是第三种,(我们通过是使用classPath是吧)
location转为Resource的具体方法就是:getResources() 第一次可以深入看一下。入口在AbstractApplicationContext中

public Resource[] getResources(String locationPattern) throws IOException {
   return this.resourcePatternResolver.getResources(locationPattern);
}

 然后调用pattern,资源匹配器,PathMatchingResourcePatternResolver类中 第二次

    public Resource[] getResources(String locationPattern) throws IOException {
   if (locationPattern.startsWith(CLASSPATH_ALL_URL_PREFIX)) { //classpath*: 多个xml扫描
      // a class path resource (multiple resources for same name possible)
      if (getPathMatcher().isPattern(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()))) {
         // a class path resource pattern
         return findPathMatchingResources(locationPattern);
      }
      else {
         // all class path resources with the given name
         return findAllClassPathResources(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()));
      }
   }
   else {  //走的这里,单个xml
      //分离classpath,得到真正的值
      int prefixEnd = (locationPattern.startsWith("war:") ? locationPattern.indexOf("*/") + 1 :
            locationPattern.indexOf(':') + 1);
      if (getPathMatcher().isPattern(locationPattern.substring(prefixEnd))) {
         // a file pattern
         return findPathMatchingResources(locationPattern);
      }
      else { //走这里,单一xml
         return new Resource[] {getResourceLoader().getResource(locationPattern)}; //【入】DefaultResourceLoader类中
      }
   }
}

 getResource() 第三次DefaultResourceLoader类中

public Resource getResource(String location) {
    //这是一个集合,size=0,直接掠过
   for (ProtocolResolver protocolResolver : this.protocolResolvers) {
      Resource resource = protocolResolver.resolve(location, this);
      if (resource != null) {
         return resource;
      }
   }

   if (location.startsWith("/")) {
      return getResourceByPath(location);
   }
   else if (location.startsWith(CLASSPATH_URL_PREFIX)) { //走这里 classpath:
      return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader());
   }
   else {
      try {
         // Try to parse the location as a URL...
         URL url = new URL(location);
         return (ResourceUtils.isFileURL(url) ? new FileUrlResource(url) : new UrlResource(url));
      }
     ...
}

这个new出来的类,ClassPathResource类,包含三个字段,内容简单,就是location的封装。

public class ClassPathResource extends AbstractFileResolvingResource {
	private final String path; //xml 路径
	@Nullable
	private ClassLoader classLoader;
	@Nullable
	private Class<?> clazz;
。。。。
	}

现在我们拿到了Resource。

又回到了loadBeanDefinitions(resources)里面;AbstractBeanDefinitionReader
loadBeanDefinitions 第六次 传入 Resources,它也是一个入口,对数组的切分。

public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
   int counter = 0;
   for (Resource resource : resources) {
      counter += loadBeanDefinitions(resource);
   }
   return counter;
}

loadBeanDefinitions(resource) 第七次:进入XmlBeanDefinitionReader类中:

public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
   return loadBeanDefinitions(new EncodedResource(resource));
}

 将resource再一次封装,成为EncodedResource

// 对resource 的封装
public class EncodedResource implements InputStreamSource {
	private final Resource resource;
	@Nullable
	private final String encoding;
	@Nullable
	private final Charset charset;
...
}

loadBeanDefinitions(encodedResource)  第八次 还在XmlBeanDefinitionReade类r中

public int loadBeanDefinitions(EncodedResource encodedResource)  {
		...
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();
   }
}

到这里 loadBeanDefinitions()于结束了,开始的是doLoad…了。!!

首先,获得输入流,读取xml文件,通过ClassPathResource类的getInputStream()方法:三种方式,我们看一种就可以了

	public InputStream getInputStream() throws IOException {
		InputStream is;
		if (this.clazz != null) {
			is = this.clazz.getResourceAsStream(this.path);
		}
		...
		return is;
	}

然后就是doLoadBeanDefinitions()方法:

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) {
   try {
      //将 resource 转成 Document(xml,html对象)
      Document doc = doLoadDocument(inputSource, resource);
      return registerBeanDefinitions(doc, resource); //【入】
   }
...
}

从doLoad 升级成为 registereBeanDefinitions()了,这里进展很快啊。document的获取jdk自带了,这里不讲述。

public int registerBeanDefinitions(Document doc, Resource resource) throws {
   BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
   //获取容器中注册的bean数量
   int countBefore = getRegistry().getBeanDefinitionCount();
   //解析入口 【委派】,
   documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
   //计算该 resource对应xml 中包含的bean数量
   return getRegistry().getBeanDefinitionCount() - countBefore;
}

registerBeanDefinitions() 第二次,位于DefaultBeanDefinitionDocumentReader类

它的第二个参数 XmlReaderContext对象:

// readerContext 读取内容
public class XmlReaderContext extends ReaderContext {
	private final XmlBeanDefinitionReader reader;
	private final NamespaceHandlerResolver namespaceHandlerResolver;
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
		this.readerContext = readerContext;
		//xml的根元数,这个就是java读取xml的内容。树状的模式。
		Element root = doc.getDocumentElement();
		doRegisterBeanDefinitions(root);//【深入】
	}

从load->doLoad->register->doRegister 结束,现在开始从xml的对象模型document的根节点root开始遍历,

protected void doRegisterBeanDefinitions(Element root) {
		//具体 的解析过程由,BeanDefinitionoParserDelegate实现。,它的里面,定义了spring bean的各种xml元素。
		BeanDefinitionParserDelegate parent = this.delegate;
		this.delegate = createDelegate(getReaderContext(), root, parent);         
		...
		//在解析之前,可以增加自定义解析,包括解析之后,这样 用户可以扩展spring的解析。
		preProcessXml(root);
		parseBeanDefinitions(root, this.delegate);  //【重点】--深入,解析spring bean xml的具体过程,(各种子元素)
		postProcessXml(root);
		this.delegate = parent;
	}

 从doregister 跳转到了parseBeanDefinitions了,很不错,这里传入的root根节点,这里就开始对document进行解析了,递归方式的,一个一个的解析,很多内容,也是真正执行功能的代码,对标签比如说bean,import,location,alias,等等进行解析,内容太多,这里就不叙述了。

 解析完毕之后,得到BeanDefinitionHolder(就是对beanDefinition的封装),注册进入IOC容器,也就是BeanFactory.
此时IOC容器里面存放了bean的全部配置信息。实现了控制反转。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值