加载xml配置文件
mian()入口
//读取xx.xml文件 ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("xx.xml");
ClassPathXmlApplicationContext
public ClassPathXmlApplicationContext( String[] configLocations, boolean refresh, @Nullable ApplicationContext parent) throws BeansException { //1.调用父类构造方法,初始化一些成员属性 super(parent); //2.设置配置路径 setConfigLocations(configLocations); if (refresh) { //3.大名鼎鼎的refresh方法 refresh(); } }
点击diagrams图 先挂着待会有用
设置配置路径
AbstractRefreshableConfigApplicationContext
public void setConfigLocations(@Nullable String... locations) { if (locations != null) { Assert.noNullElements(locations, "Config locations must not be null"); this.configLocations = new String[locations.length]; for (int i = 0; i < locations.length; i++) { //resolvePath 解析路径 this.configLocations[i] = resolvePath(locations[i]).trim(); } } else { this.configLocations = null; } }
此时,configLocations已经放在 AbstractRefreshableConfigApplicationContext
待会用到
resolvePath里面的getEnvironment().resolveRequiredPlaceholders(path)
,这里会寻找Environment map中替换字符,如${name}
如果environment为空,则创建environment。return new StandardEnvironment();
读取BeanDefinitionsrefresh();
先跳过prepareRefresh
,
进去obtainFreshBeanFactory()
再
进去refreshBeanFactory()
看diagrams图
看AbstractApplicationContext
的子类
AbstractRefreshableConfigApplicationContext
/** * 省略的注解,记得看 */ @Override protected final void refreshBeanFactory() throws BeansException { if (hasBeanFactory()) { destroyBeans(); closeBeanFactory(); } try { //创建BeanFactory DefaultListableBeanFactory重点,之后会经常看到 DefaultListableBeanFactory beanFactory = createBeanFactory(); //设置序列化id beanFactory.setSerializationId(getId()); //自定义或定制beanFactory,设置相关Property customizeBeanFactory(beanFactory); //这节重点 读取BeanDefinitions loadBeanDefinitions(beanFactory); this.beanFactory = beanFactory; } catch (IOException ex) { throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex); } }
看diagrams图
AbstractRefreshableConfigApplicationContext
的子类 AbstractXmlApplicationContext
别走散了
/** * 省略的注解,记得看 */ @Override 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. // 初始化BeanDefinitionReader initBeanDefinitionReader(beanDefinitionReader); 重点 读取BeanDefinitions loadBeanDefinitions(beanDefinitionReader); } /** * 省略的注解,记得看 */ protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException { Resource[] configResources = getConfigResources(); if (configResources != null) { reader.loadBeanDefinitions(configResources); } //getConfigLocations() 点进去会发现跳到父类,这个就是刚刚setConfigLocations()时候放在父类 String[] configLocations = getConfigLocations(); if (configLocations != null) { 加载loadBeanDefinitions reader.loadBeanDefinitions(configLocations); } }
这里要小心点,会容易乱,需要连续点击loadBeanDefinitions,
记住一开始是在AbstractXmlApplicationContext
我贴出来的代码上,记得看入参,
搜索loadBeanDefinitions 让他持续高亮,准备,开始
AbstractBeanDefinitionReader
类,循环把location list 提取单个location
return loadBeanDefinitions(location, null);
点击,通过debug发现是第一个,进去(后面解释) 关于第2点解释
Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location); int count = loadBeanDefinitions(resources);
又是循环,别急,看入参Resource… resources,
这里会有多个子类,选子类XmlBeanDefinitionReader
进去,不懂的先跟我点进去(后面解释) 关于第4点解释
继续点击,恭喜你,进入到真正的xml读取类 XmlBeanDefinitionReader
读取类 XmlBeanDefinitionReader
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException { ... // 省略的代码 ... 熟悉吧 读取io流 这里一定要会啊 try (InputStream inputStream = encodedResource.getResource().getInputStream()) { InputSource inputSource = new InputSource(inputStream); if (encodedResource.getEncoding() != null) { inputSource.setEncoding(encodedResource.getEncoding()); } 读取完还需要解析xml文件 return doLoadBeanDefinitions(inputSource, encodedResource.getResource()); } catch (IOException ex) { throw new BeanDefinitionStoreException( "IOException parsing XML document from " + encodedResource.getResource(), ex); ... // 省略的代码 ... }
以防有人不懂,其实怕我之后自己不记得的ヾ(≧▽≦*)o,
关于第4点
loadBeanDefinitions(XmlBeanDefinitionReader reader)
开局第一句写的就是,如果没有debug第一次看绝对会晕。
关于第2点
建议怼着源码看 防止走散,第2点的
AbstractBeanDefinitionReader
类中loadBeanDefinitions()
,建议打个断点,一行行看
public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException
首先,该方法获取资源加载器(ResourceLoader
)对象,如果资源加载器为null
,则抛出BeanDefinitionStoreException
异常,提示无法从指定位置加载Bean定义。
接下来,通过判断资源加载器是否实现了ResourcePatternResolver
接口来确定是否支持资源模式匹配。如果支持资源模式匹配,则使用资源加载器的getResources
方法获取符合指定位置模式的所有资源。
如果资源加载器不支持资源模式匹配,则假定指定位置为单个资源的绝对URL,并通过资源加载器的getResource
方法获取该资源。
在获取资源后,调用loadBeanDefinitions
方法重载的形式进行具体的Bean定义加载操作。该方法将返回加载的Bean定义数量。
如果提供了actualResources
参数(一个可选的Set<Resource>
集合),则将加载的资源添加到该集合中。
最后,根据需要输出日志,表示从指定位置成功加载了多少个Bean定义,并返回加载的Bean定义数量。
总体而言,该方法根据指定的位置加载Bean定义。如果资源加载器支持资源模式匹配,则可以加载符合指定模式的多个资源;否则,仅加载指定位置的单个资源。加载的Bean定义将在后续的处理中用于创建和管理相应的Bean实例。