Spring源码总结
我们会在 web.xml 中配置 ContextLoaderListener 监听器,当 Tomcat 启动时,会触发 ContextLoaderListener 的 contextInitialized 方法,但是在执行该方法前,会去先执行父类的静态代码块
1 | static { |
这段代码主要作用是,根据spring-web工程下的ContextLoader.properties文件构建ClassPathResource,然后根据文件内容初始化ContextLoader的defaultStrategies属性;
接下来就回去执行ContextLoaderListener的contextInitialized方法:
1 | /** |
该方法的核心内容在第3步:配置和刷新web应用上下文(见下面代码块),
其中第2步:创建一个WebApplicationContext并保存到context属性会用到defaultStrategies属性
1 | protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) { |
4.设置 wac 的 configLocations 属性值为 configLocationParam,
5.初始化属性源,见代码块11详解。
6.自定义上下文,见代码块12详解。
7.应用上下文的刷新,IoC 核心内容
总结:初始化IOC容器之前的一些操作:
1.根据spring-web工程下的ContextLoader.properties文件构建ClassPathResource,然后根据文件内容初始化ContextLoader的defaultStrategies属性;
2.根据defaultStrategies属性创建WebApplicationContext,这里创建的是XmlWebApplicationContext并强转为ConfigurableWebApplicationContext;
3.将web.xml配置文件中的属性都初始化到ConfigurableWebApplicationContext中,创建并初始化环境属性;
4.将ServletContext替换掉ConfigurableWebEnvironment环境中的占位符;
5.自定义上下文,开发者可以自己创建ApplicationContextInitializer的实现类,重写initialize,向ConfigurableApplicationContext上下文中添加自己的逻辑实现;并在web.xml中定义 contextInitializerClasses 或 globalInitializerClasses 参数,参数值为自定义实现类的全路径;此处是Spring提供的自定义应用上下文 ConfigurableApplicationContext 的扩展点;
6.执行应用上下文的刷新,也就是核心方法refresh()方法;
obtainFreshBeanFactory方法:
总结:
1.创建DefaultListableBeanFactory;
2.为指定BeanFactory创建XmlBeanDefinitionReader;
3.获取配置文件路径集合,遍历文件路径;(就是为了将配置文件封装成Resource对象和获取到inputStream)
3.1.根据路径拿到该路径下所有符合的配置文件,并封装成Resource集合;
3.2.遍历Resource集合,构建EncodedResource,并获取到Resource的inputStream
3.3.将inputStream封装成org.xml.sax.InputSource
4.解析配置文件,注册Bean;
4.1.根据inputSource和resource加载XML文件,并封装成Document
4.1.1.获取XML配置文件的验证模式。XML文件的验证模式是用来保证XML文件的正确性,常见的验证模式有两种:DTD和XSD
4.2.根据返回的Document注册Bean信息(对配置文件的解析,核心逻辑)
4.2.1.根据resource创建一个XmlReaderContext,用于存放解析时会用到的一些上下文信息,并且会去构建NamespaceHandlerResolver,DefaultNamespaceHandlerResolver的handlerMappingsLocation属性会使用默认的值”META-INF/spring.handlers”,
并且这边有个重要的属性 handlerMappings,handlerMappings 用于存放命名空间和该命名空间handler类的映射
4.2.2.根据Document的节点信息和XmlReaderContext构建BeanDefinitionParserDelegate,并赋值给DefaultBeanDefinitionDocumentReader的delegate属性;
4.2.3.解析处理profile属性,profile属性主要用于多环境开发,可以在配置文件中同时写上多套配置来适用于开发环境、测试环境、生产环境,这样可以方便的进行切换开发、部署环境,最常用的就是更换不同的数据库。具体使用哪个环境在 web.xml 中通过参数 spring.profiles.active 来配置;
4.2.4.解析前处理, 留给子类实现;
4.2.5.解析并注册bean定义;
4.2.5.1.遍历root的子节点列表;
4.2.5.1.如果节点的命名空间是 Spring 默认的命名空间,则走 parseDefaultElement(ele, delegate) 方法进行解析,例如最常见的:
4.2.5.2.如果节点的命名空间不是 Spring 默认的命名空间,也就是自定义命名空间,则走 delegate.parseCustomElement(ele) 方法进行解析,例如常见的:context:component-scan/、aop:aspectj-autoproxy/;
4.2.6.解析后处理, 留给子类实现;
parseDefaultElement(ele, delegate)总结:
先将 xml 中的 bean 配置信息进行了解析,并构建了 AbstractBeanDefinition(GenericBeanDefinition) 对象来存放所有解析出来的属性。
然后,我们将 AbstractBeanDefinition 、beanName、aliasesArray 构建成 BeanDefinitionHolder 对象并返回。
最后,我们通过 BeanDefinitionHolder 将 BeanDefinition 和 beanName 注册到 BeanFactory 中,也就是存放到缓存中。
执行完 parseDefaultElement 方法,我们得到了两个重要的缓存:
beanDefinitionNames 缓存;
beanDefinitionMap 缓存;
aliasMap缓存;
beanDefinitionMap:DefaultListableBeanFactory的属性,用于存放bean name与BeanDefinition的映射关系
1 | /** Map of bean definition objects, keyed by bean name. */ |
beanDefinitionNames:DefaultListableBeanFactory的属性,用于存放bean name集合
1 | /** List of bean definition names, in registration order. */ |
aliasMap:SimpleAliasRegistry的属性,用于别名和beanName的映射
1 | /** Map from alias to canonical name. */ |
BeanDefinition是什么:
BeanDefinition主要是用来描述Bean,存储Bean的相关信息,主要包括:Bean的属性、是否单例、延迟加载、Bean的名称、构造方法等;
1.BeanDefinition继承了AttributeAccessor,具备处理属性的能力;
2.BeanDefinition继承了BeanMetadataElement,可以持有Bean元数据元素,作用是可以持有XML文件的一个bean标签对应的Object;