XML标签解析过程
从ClassPathXmlApplicationContext.refresh()方法进入
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
prepareRefresh();
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
.........
}
进入obtainFreshBeanFactory(),重点看 refreshBeanFactory()
初始化BeanFactory ,将xml文件中的标签封装成BeanDefinition
@Override
protected final void refreshBeanFactory() throws BeansException {
//如果BeanFactory不为空,则清除BeanFactory和里面的实例
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
//BeanFactory 实例工厂
DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId());
//设置是否可以循环依赖 allowCircularReferences
//是否允许使用相同名称重新注册不同的bean实现.
customizeBeanFactory(beanFactory);
//解析xml,并把xml中的标签封装成BeanDefinition对象
loadBeanDefinitions(beanFactory);
this.beanFactory = beanFactory;
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}
继续往下看,如何把标签解析封装成BeanDefinition
在loadBeanDefinitions方法中创建xml的解析器,将解析器对象传进去,从loadBeanDefinitions()一直点下去,开始解析xml文件
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
....
....
beanDefinitionReader.setResourceLoader(this);//解析器中传入this进去,因为ApplicationContext是实现了ResourceLoader接口的
loadBeanDefinitions(beanDefinitionReader);
获取遍历所有的xml文件
String[] configLocations = getConfigLocations();//获取本地xml文件
if (configLocations != null) {
reader.loadBeanDefinitions(configLocations);
}
public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
ResourceLoader resourceLoader = getResourceLoader();
................
if (resourceLoader instanceof ResourcePatternResolver) {
try {
Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
//主要看这个方法
int count = loadBeanDefinitions(resources);
...................
}
一直点下去,到 XmlBeanDefinitionReader类,看loadBeanDefinitions方法
//获取Resource对象中的xml文件流对象
try (InputStream inputStream = encodedResource.getResource().getInputStream()) {
//InputSource是jdk中的sax xml文件解析对象
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
//主要看这个方法
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
}
重点来了
do方法点进去,看 registerBeanDefinitions()方法,根据解析出来的doc对象,拿到里面的标签
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
int countBefore = getRegistry().getBeanDefinitionCount();
//createReaderContext(resource)封装了XmlBeanDefinitionReader对象
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
return getRegistry().getBeanDefinitionCount() - countBefore;
}
继续往下看,通过doc拿到root节点,看 parseBeanDefinitions()方法,遍历解析标签,默认标签和自定义标签
默认标签解析
parseDefaultElement(ele, delegate);
重点看我们熟悉的bean标签,processBeanDefinition方法点进去,看parseBeanDefinitionElement方法
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
//import标签解析
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
importBeanDefinitionResource(ele);
}
//alias标签解析 别名标签
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
processAliasRegistration(ele);
}
//bean标签
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
processBeanDefinition(ele, delegate);
}
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
// recurse
doRegisterBeanDefinitions(ele);
}
}
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);解析标签,封装成bd
方法点进去,就不贴代码了,就是拿到标签里的id、name、class、singleton、scope、lazy-init、depends-on、factory-method、factory-bean等属性值,很多,就不一一写出来了
记住factory-method、factory-bean这两个属性,@Bean注解就是用的这个思路
几乎所有的注解的实现都可以和传统的xml标签对应上
再回过头看自定义标签的解析,先这个方法
delegate.parseCustomElement(ele);
自定义标签要比默认标签要复杂一些
public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
String namespaceUri = getNamespaceURI(ele);获取自定义标签的 namespace 命令空间,什么是namespaceUri,就是我们xml文件最上面的引入的“http://www.springframework”这段东西
if (namespaceUri == null) {
return null;
}
NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
if (handler == null) {
error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
return null;
}
return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}
resolve方法点进去看
public NamespaceHandler resolve(String namespaceUri) {
Map<String, Object> handlerMappings = getHandlerMappings();//获取spring所有jar包里面“META-INF/spring.handlers”文件,并建立映射关系
Object handlerOrClassName = handlerMappings.get(namespaceUri);//根据uri获取到这个命名空间的处理类
if (handlerOrClassName == null) {
return null;
}
else if (handlerOrClassName instanceof NamespaceHandler) {
return (NamespaceHandler) handlerOrClassName;
}
else {
String className = (String) handlerOrClassName;//获取classname
try {
Class<?> handlerClass = ClassUtils.forName(className, this.classLoader);//拿到这个class
if (!NamespaceHandler.class.isAssignableFrom(handlerClass)) {
throw new FatalBeanException("Class [" + className + "] for namespace [" + namespaceUri +
"] does not implement the [" + NamespaceHandler.class.getName() + "] interface");
}
NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass);//实例化这个处理类
namespaceHandler.init();//执行他的init方法,下面截个图看看有都有哪些
handlerMappings.put(namespaceUri, namespaceHandler);
return namespaceHandler;
}
......................
}
}
看到这个图,都熟悉的aop、jdbc、mvc等处理类
我们看一下处理类里面是些什么东西,熟悉的标签,registerBeanDefinitionParser方法会把标签及对应的处理类方法一个map中
回头来看调用parse方法,看一下component-scan标签是怎么解析的,我们常说的扫描就是从这里开始的
public BeanDefinition parse(Element element, ParserContext parserContext) {
String basePackage = element.getAttribute(BASE_PACKAGE_ATTRIBUTE);//获取标签值
basePackage = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(basePackage);
String[] basePackages = StringUtils.tokenizeToStringArray(basePackage,
ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);//可以以逗号分开
// Actually scan for bean definitions and register them.
ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);//创建注解扫描器,点进去可以看到,添加了@component注解的过滤集合,贴个截图
Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);//doscan执行扫描
registerComponents(parserContext.getReaderContext(), beanDefinitions, element);//执行一些比较重要的后置处理
return null;
}
后面方法体代码比较多,就不贴很全了
比较重要的doscan方法,扫描包路径下所有的class文件,并过滤出@service等注解的类,所有继承了@component的注解
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
Assert.notEmpty(basePackages, "At least one base package must be specified");
Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
for (String basePackage : basePackages) {
Set<BeanDefinition> candidates = findCandidateComponents(basePackage);//扫描有注解的类,并封装成bd对象,主要是拿到包路径下所有的class文件,并match判断一下有没有过滤集合中的注解,不点进去看了
//只需注意一点,这里的bd是scannedGenericBeanDefinition,其中封装了class的一些元数据,包括注解信息等,match注解以及下面的注解支持都基于元数据
for (BeanDefinition candidate : candidates) {
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
candidate.setScope(scopeMetadata.getScopeName());//设置scope值
String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);//获取beanName,如果注解中没有指定,则取className
if (candidate instanceof AbstractBeanDefinition) {
postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
}
if (candidate instanceof AnnotatedBeanDefinition) {
AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);//支持了@Lazy、dependsOn等注解,可以点进去看一下
}
if (checkCandidate(beanName, candidate)) {
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);//封装成熟悉的bdh对象
definitionHolder =
AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
beanDefinitions.add(definitionHolder);
registerBeanDefinition(definitionHolder, this.registry);//注册,和上面说到的默认标签同样
}
}
}
return beanDefinitions;
}
自定义标签的bd对象收集已经结束了,这里只是以component-scan为例。
回过头看一下非常重要的registerComponents方法,这几个后置处理器后面再说
方法里面注册了几个比较重要的如 ConfigurationClassPostProcessor(会扫描@Configuration @Component @Bean 注解的解析)、 AutowiredAnnotationBeanPostProcessor(会扫描@Value 和@Autowired 注解)、
CommonAnnotationBeanPostProcessor
到这里为止,我们xml解析已经完成了,并封装成了bd对象,下一步就是要把bd对象注册到我们的BeanFactory中
再回到 processBeanDefinition()方法中,看下面这个方法
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
看最后的一段代码
// Still in startup registration phase
this.beanDefinitionMap.put(beanName, beanDefinition);
this.beanDefinitionNames.add(beanName);
把beanName作为key,bd作为value,放到db的map中
把beanName放到beanDefinitionNames 集合中,这个list要记住,bean实例化的时候需要用到
到这里,xml标签的解析就结束了