spring源码之标签解析

导读:本篇文章讲解 spring源码之标签解析,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

AbstractApplicationContext类中refresh()方法为spring容器启动的核心方法,是spring容器启动的核心流程,是一个典型的父类模板设计模式,根据不同的上下文对象,会调用到不同的上下文对象子类方法中。
核心的上下文子类有
1、ClassPathXmlApplicationContext(标签)
2、FileSystemXmlApplicationContext(导入)
3、AnnotationConfigApplicationContext(注解)
4、EmbeddedWebApplicationContext(springboot)

@Override
public void refresh() throws BeansException, IllegalStateException {
	synchronized (this.startupShutdownMonitor) {
		// Prepare this context for refreshing.
		prepareRefresh();

		// Tell the subclass to refresh the internal bean factory.
		ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

		// Prepare the bean factory for use in this context.
		prepareBeanFactory(beanFactory);
}

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
	refreshBeanFactory();
	return getBeanFactory();
}

obtainFreshBeanFactory方法的作用总结:

1、创建BeanFactory对象
2、xm解析:传统标签解析、自定义标签解析,比如<context:component-scan …>
3、把解析的xml标签封装成BeanDefinition对象

@Override
protected final void refreshBeanFactory() throws BeansException {
	if (hasBeanFactory()) {
		destroyBeans();
		closeBeanFactory();
	}
	try {
		DefaultListableBeanFactory beanFactory = createBeanFactory();//创建bean工厂
		beanFactory.setSerializationId(getId());
		customizeBeanFactory(beanFactory);//设置是否允许循环依赖和相同名称的BeanDefinition是否允许覆盖
		loadBeanDefinitions(beanFactory);//解析xml,并把xml中的标签封装成BeanDefinition
		this.beanFactory = beanFactory;
	}
	catch (IOException ex) {
		throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
	}
}

xml解析过程:
1、把字符串类型的xml文件路径,形如:classpath*:user/**/*-context.xml,转换成Resource对象类型,其实就是用流的方式加载配置文件,然后封装成Resource对象
2、获取Resource对象中的xml文件流对象,并封装成InputSource对象
3、把InputSource解析成Document文件对象(采用的是sax解析,按sax解析规则解析成Document)
4、将Document对象的root节点作为参数进行解析,如图所示:

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
		if (delegate.isDefaultNamespace(root)) {
			NodeList nl = root.getChildNodes();
			for (int i = 0; i < nl.getLength(); i++) {
				Node node = nl.item(i);
				if (node instanceof Element) {
					Element ele = (Element) node;
					if (delegate.isDefaultNamespace(ele)) {
						//默认标签解析
						parseDefaultElement(ele, delegate);
					}
					else {
						//自定义标签解析
						delegate.parseCustomElement(ele);
					}
				}
			}
		}
		else {
			delegate.parseCustomElement(root);
		}
	}

一、自定义标签解析流程
以<context:component-scan base-package…>为例:

1、根据当前解析标签的头信息找到对应的namespaceUri
2、加载spring所有jar中spring.handlers文件,并建立映射关系
3、根据namespaceUri从映射关系中找到对应的实现了NameSpaceHandler接口的类
4、调用init()方法并返回NamespaceHandler(注:init()注册了各种自定义标签的解析类)
5、调用NamespaceHandler(和namespaceUri进行了映射)根据标签注册的解析类(ComponentScanBeanDefinitionParser)的parse()方法
6、获取base-package属性
7、创建注解扫描器ClassPathBeanDefinitionScanner
8、扫描路径下有@Component注解的class文件(@Controller、@Service等父注解为@Component;spring注册了注解类型过滤,将@Component注册到了集合includeFilters中,扫描时会根据includeFilters注册的注解过滤),并封装成ScannedGenericBeanDefinition放入set集合
9、循环set集合,将ScannedGenericBeanDefinition封装成BeanDefinitionHolder并注册BeanDefinition
10、注册组件:

ConfigurationClassPostProcessor
AutowiredAnnotationBeanPostProcessor
CommonAnnotationBeanPostProcessor
@Nullable
public BeanDefinition parse(Element element, ParserContext parserContext) {
	//获取basePackage属性
	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);
	//扫描并把扫描的类封装成beanDefinition对象
	Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);
	//注册组件
	registerComponents(parserContext.getReaderContext(), beanDefinitions, element);

	return null;
}
...
...

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) {
			//扫描到有注解的类并封装成BeanDefinition对象
			Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
			for (BeanDefinition candidate : candidates) {
				ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
				candidate.setScope(scopeMetadata.getScopeName());
				String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
				if (candidate instanceof AbstractBeanDefinition) {
					postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
				}
				if (candidate instanceof AnnotatedBeanDefinition) {
					//支持了@Lazy @DependOn注解
					AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
				}
				if (checkCandidate(beanName, candidate)) {
					BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
					definitionHolder =
							AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
					beanDefinitions.add(definitionHolder);
					//BeanDefinition注册
					registerBeanDefinition(definitionHolder, this.registry);
				}
			}
		}
		return beanDefinitions;
	}

二、默认标签解析流程
以<bean …>为例:

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
		//解析<bean>,封装成BeanDefinition
		BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
		if (bdHolder != null) {
			bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
			try {
				//完成document到BeanDefinition对象转换后,对BeanDefinition对象进行缓存注册
				// Register the final decorated instance.
				BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
			}
			catch (BeanDefinitionStoreException ex) {
				getReaderContext().error("Failed to register bean definition with name '" +
						bdHolder.getBeanName() + "'", ele, ex);
			}
			// Send registration event.
			getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
		}
	}

解析bean标签,封装成BeanDefinition过程:
1、获取bean标签的id和name属性;
2、检查beanName是否重复;
3、创建GenericBeanDefinition,完成其他属性解析并设置到BeanDefinition中,如图所示:

@Nullable
	public AbstractBeanDefinition parseBeanDefinitionElement(
			Element ele, String beanName, @Nullable BeanDefinition containingBean) {
		...
		//解析出className和parent,className为需要实例化的类
		...

		try {
			//创建GenericBeanDefinition对象
			AbstractBeanDefinition bd = createBeanDefinition(className, parent);
			//解析bean标签的属性,并把解析出来的属性设置到BeanDefinition中,比如init-method、depends-on等
			parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
			bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
			//解析bean中的meta标签
			parseMetaElements(ele, bd);
			//解析bean中的lookup-method标签
			parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
			//解析bean中的replaced-method标签
			parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
			//解析bean中的constructor-arg标签
			parseConstructorArgElements(ele, bd);
			//解析bean中的property标签
			parsePropertyElements(ele, bd);
			...
			...

4、完成BeanDefinition的注册

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/13831.html

(0)
小半的头像小半

相关推荐

极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!