Spring源码解读之Xml解析Annotation解析

废话不说,开始解毒Spring 对xml的解析和Annotationk的解析。
首先,Spring分为父Spring和子Spring,子Spring就是SpringMVC,父Spring的监听器是ContextLoaderListener这个Listener,SpringMVC的入口就在DispatcherServlet。从ContextLoadListener开始解毒吧。
ContextLoaderListener的父类方法ContextLoader#initWebApplicationContext()->ContextLoader#configureAndRefreshWebApplicationContext()->AbstractApplicationContext#refresh()
首先解读ApplicationContext中的refresh()方法。

1.1 prepareRefresh() 方法

设置容器的一些状态:设置容器处于未关闭状态,设置active为激活状态,注入环境

1.2 AbstractApplicationContext#obtainFreshBeanFactory() 方法(xml解析bean标签)

调用到AbstractRefreshableApplicationContext#obtainFreshBeanFactory()方法
调用到AbstractRefreshableApplicationContext#refreshBeanFactory()方法
调用到AbstractXmlApplicationContext#loadBeanDefinitions()方法
调用到AbstractXmlApplicationContext#loadBeanDefinitions()方法

调用到XmlBeanDefinitionReader#loadBeanDefinitions()方法
在loadBeanDefinitions方法中获取spring的配置文件流
然后调用到XmlBeanDefinitionReader#doLoadBeanDefinitions()
把xml转换成Document对象
调用到XmlBeanDefinitionReader#registerBeanDefinitions()方法
调用到DefaultBeanDefinitionDocumentReader#parseBeanDefinitions()方法
############################################################################################
此方法中有一个对子元素的for循环会调到此类中的#parseDefaultElement()方法
如果nodename 等于 bean 的话 也就是bean 的解析,对应着processBeanDefinition()方法,会在此进行解析
下面会跟到BeanDefinitionParserDelegate#parseBeanDefinitionElement();
重点解释parseBeanDefinitionElement()方法(开头用@表示,@1表示第一步…):
@1首先获取bean的id和name,对name进行别名处理
@2到本类的parseBeanDefinitionElement()方法:(开头用*表示,*1表示第一步…):

*1在本类的parseState栈中push一个beanname
*2创建一个AbstractBeanDefinition类型
*3parseBeanDefinitionAttributes()方法是对bean设置是否单例,是否懒加载

@3对AbstractBeanDefinition对象封装成BeanDefinitionHolder对象进行返回
############################################################################################
以上##框起来的部分只是DefaultBeanDefinitionDocumentReader#processBeanDefinition()方法的第一行,这里返回一个BeanDefinitionHolder对象
下面接着看:
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
这一行代码是对BeanDefinition的一个注册
进入到BeanDefinitionReaderUtils#registerBeanDefinition()方法
在跟入到DefaultListableBeanFactory#registerBeanDefinition()方法,下面重点解释这个方法(以*开头,*1代表第一步):
*1.这里的oldBeanDefinition对象肯定为空直接走else,往当前map里写入this.beanDefinitionMap.put(beanName, beanDefinition)
并且把beanName写入到名字为beanDefinitionNames的list中
把beanName也写入到名字为manualSingletonNames的list中
到这里BeanDefinitionReaderUtils#registerBeanDefinition()方法已经执行完了registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
行下面该注册别名在SimpleAliasRegistry类的aliasMap中注册别名,这里就不细说了。
到此,AbstractApplicationContext#refresh()方法的obtainFreshBeanFactory()方法主要bean执行完毕

1.3 Annotation标签解析

抛开以上思路,上面解释了xml解析bean。下面解释解析Annotation标签。我先介绍怎么手写Spring插件:
首先,在META-INF文件夹下建立两个文件,名字分别为spring.handlers,spring.schemas

1.3.1 spring.handlers文件

http://www.lgq.com/mt/tag=com.custom.tag.www.customTag.handel.NameSpaceHandel
就这么一行信息等号前面的url是自定义的,后面的类是继承自NamespaceHandlerSupport类的手写类,其中有个init方法
registerBeanDefinitionParser(“reference”, new ReferenceBeanDefiniitonparser(Reference.class));
自定义一个ReferenceBeanDefiniitonparser类,实现BeanDefinitionParser接口,实现paBeanDefinition方法,其中Element对象就是reference 标签的封装对象,
ParserContext是对Spring容器的扩展,自定义一个BeanDefinition对象,通过parserContext.getRegistry().registerBeanDefinition(“reference”+id, beanDefinition);
就可以注入Spring

1.3.2 spring.schemas

http://www.lgq.com/mt/tag/mytag.xsd=com/custom/tag/www/customTag/config/mytag.xsd
也是一行信息,主要的是对应的mytag.xsd(名字随便起的)文件
mytag.xsd文件

<xsd:element name="reference">
<xsd:complexType >
<xsd:attribute name="id" type="xsd:string" use="required" />
<xsd:attribute name="interface" type="xsd:string"></xsd:attribute>
</xsd:complexType>
</xsd:element>

可以定义多个对象
下面就是熟悉的spring的配置文件了

1.3.3 在顶部的beans里增加两行信息
xmlns:nihao=”http://www.lgq.com/mt/tag” 和http://www.lgq.com/mt/tag http://www.lgq.com/mt/tag/mytag.xsd 信息
到此,就可以在spring的配置文件中使用自定义的标签
<nihao:reference id="userService" interface="com.custom.tag.www.customTag.service.UserService" ></dubbo:reference>
是spring自定义标签的内容(dubbo和Spring无缝融接就是这么干的)
下面我解释怎么进行Annotation标签的扫描

1.3.4 Annotation解析bean

首先打开ContextNamespaceHandler类(继承了NamespaceHandlerSupport类)
你应该已经看到了“component-scan”标签了,这就是spring配置文件里面的扫描包文件的标签
跟入到ComponentScanBeanDefinitionParser(此类实现了BeanDefinitionParser接口)#parse()方法
下面1.3.5和1.3.6和1.3.7 中解释下列三

ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);
Set beanDefinitions = scanner.doScan(basePackages);
registerComponents(parserContext.getReaderContext(), beanDefinitions, element);

1.3.5 解释configureScanner(parserContext, element);

跟入到ComponentScanBeanDefinitionParser#configureScanner()方法,看到有一个判断是否有use-default-filters属性的判断,如果此属性为false的话,是不会进行
Annotion的扫描的:
跟到 createScanner(parserContext.getReaderContext(),` useDefaultFilters);方法在跟到
if (useDefaultFilters) {
registerDefaultFilters();
}
//这个useDefaultFilters就是use-default-filters的设置,默认为true,如果设置为false的话就不会执行下面这个方法就得另外配置需要扫描的标签了.
下面解释registerDefaultFilters()方法;
this.includeFilters.add(new AnnotationTypeFilter(Component.class));
这一行代码就是吧ClassPathScanningCandidateComponentProvider#includeFilters的list里面添加需要扫描的标签,其实@Controller @Service @Repository 标签都是继承自Component 没有什么区别的
下面再看parseTypeFilters(element, scanner, parserContext); 方法
如果use-default-filters设置为false的话,就得配置include-filter 标签,同样也会加入到ClassPathScanningCandidateComponentProvider#includeFilters的list中去,还有一个exclude-filter标签(排除标签)
到这里configureScanner(parserContext, element);这句话就解释完毕。

1.3.6 解释Set beanDefinitions = scanner.doScan(basePackages);

首先看到了doScan方法,里面会调用findCandidateComponents(basePackage);下面解释findCandidateComponents方法,(用%表示%1表示第一步…)
%1.首先会把com.study.www这种包名中的.换成/,
%2.会递归调用所有包下面的文件,具体递归实现在PathMatchingResourcePatternResolver#findPathMatchingResources()方法里
%3.MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(resource); 执行到这里的时候会对Resource进行读取流
看SimpleMetadataReader(Resource resource, ClassLoader classLoader) 的构造方法就可以知道吧Resource读取成流,然后封装成 AnnotationMetadataReadingVisitor对象
%4.ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);把刚才封装成的AnnotationMetadataReadingVisitor对象进行转化成ScannedGenericBeanDefinition对象
到此这个findCandidateComponents(basePackage);就获取到了所有的ScannedGenericBeanDefinition对象
在返回到doScan()方法中,对ScannedGenericBeanDefinition对象循环
首先ScannedGenericBeanDefinition对象是实现了AnnotatedBeanDefinition接口的
所以for循环里肯定走
if (candidate instanceof AnnotatedBeanDefinition) {
AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
)代码块
这里processCommonDefinitionAnnotations 方法对bean进行一些设置,比如是否懒加载,比较容易
然后把ScannedGenericBeanDefinition转化成了BeanDefinitionHolder
再看方法registerBeanDefinition(definitionHolder, this.registry);
此方法在BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());在xml解析bean的时候分析过,也是注册到两个set,一个map中,达到了殊途同归的效果
到此,scanner.doScan(basePackages);方法就完全执行完毕

1.3.7 解释registerComponents(parserContext.getReaderContext(), beanDefinitions, element);

在registerComponents方法中看到了是否有annotation-config标签的设置,默认为true,如果设置为false的话就不会执行registerAnnotationConfigProcessors()方法了
当然,如果只是设置了component的话,即便没设置annotation-config,默认也是true的
在registerAnnotationConfigProcessors()方法中就是注入了一些bean ,比如说AutowiredAnnotationBeanPostProcessor的bean,就是对@autowire标签的支持,
在这里注入的时候和之前说的xml和Annotion注入不是走的一个方法,在这里走的是DefaultListableBeanFactory#registerBeanDefinition()方法,其实效果也是一样的,就是给两个list增加了值,一个map增加了值,相对比较简单
再就是在CompositeComponentDefinition 类的nestedComponents的list中增加了BeanComponentDefinition对象。

以上就是我所理解的Spring 解析xml 和 解析标签的源码,有错的地方还望大家指正,谢谢。
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值