上一节我们分析了配置文件的加载,我们已经了解到了spring是如何加载配置文件的。那么这一节我们开始了解一下spring是如何解析这些资源的。
public int XmlBeanDefinitionReader.loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
Assert.notNull(encodedResource, "EncodedResource must not be null");
if (logger.isInfoEnabled()) {
logger.info("Loading XML bean definitions from " + encodedResource.getResource());
}
//从当前线程中获取资源,这里的EncodedResource是对其他Resource的一种装饰,主要用于资源的编码。
Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
if (currentResources == null) {
currentResources = new HashSet<EncodedResource>(4);
this.resourcesCurrentlyBeingLoaded.set(currentResources);
}
//如果当前资源已经配置过了,那么不允许重复配置。
if (!currentResources.add(encodedResource)) {
throw new BeanDefinitionStoreException(
"Detected cyclic loading of " + encodedResource + " - check your import definitions!");
}
try {
InputStream inputStream = encodedResource.getResource().getInputStream();
try {
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
//加载资源
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
}
finally {
inputStream.close();
}
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(
"IOException parsing XML document from " + encodedResource.getResource(), ex);
}
finally {
currentResources.remove(encodedResource);
if (currentResources.isEmpty()) {
this.resourcesCurrentlyBeingLoaded.remove();
}
}
}
//XmlBeanDefinitionReader关联了一个实现了DocumentLoader的类,通过w3c,jdk自带的一个xml解析器去解析xml,在解析前会判断xml的校验模式,spring是通过xml中是否存在DOCTYPE这个标识去判断的
public Document org.springframework.beans.factory.xml.DefaultDocumentLoader.loadDocument(InputSource inputSource, EntityResolver entityResolver,
ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {
//构建DocumentBuilderFactory
DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
if (logger.isDebugEnabled()) {
logger.debug("Using JAXP provider [" + factory.getClass().getName() + "]");
}
DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
//返回Document
return builder.parse(inputSource);
}
spring通过w3c创建了Document。
public int org.springframework.beans.factory.xml.XmlBeanDefinitionReader.registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
//创建BeanDefinition文档阅读器
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
int countBefore = getRegistry().getBeanDefinitionCount();
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
return getRegistry().getBeanDefinitionCount() - countBefore;
}
到这里又创建了一个BeanDefinitionDocumentReader实例,spring各个类的关联关系变得越来越复杂,如果不做些记录的话,我们可能要迷失在代码的海洋里了,何况此时只是spring的冰山一角。那么解析来我们先看下BeanDefinitionDocumentReader的类图
这里主要提一下,在AbstractBeanDefinitionReader中关联的BeanDefinitionRegistry实际上是DefaultListableBeanFactory,这个类实现了BeanDefinitionRegistry接口,ResourceLoader实际引用的是我们分析的XmlWedApplicationContext, Environment实际是我们分析的StandardServletEnvironment
protected void doRegisterBeanDefinitions(Element root) {
//spring的解析配置文件的工作实际使用的是BeanDefinitionParserDelegate,这里类从类图上看还关联了DocumentDefaultsDefinition,这个类是用来存储beans元素上的一些默认配置,比如default-lazy-init,default-autowire
BeanDefinitionParserDelegate parent = this.delegate;
this.delegate = createDelegate(getReaderContext(), root, parent);
//判断当前元素是否spring默认命名空间的内容,如果是,就获取上面的profile元素,这个元素是用来指定默认的激活的配置
if (this.delegate.isDefaultNamespace(root)) {
String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
if (StringUtils.hasText(profileSpec)) {
String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
return;
}
}
}
//钩子函数,内部没有任何实现,子类可以覆盖它,进行一些逻辑的处理,解析BeanDefinition的前置处理
preProcessXml(root);
parseBeanDefinitions(root, this.delegate);
//钩子函数,空方法,解析完当前配置的后置处理
postProcessXml(root);
this.delegate = parent;
}
现在我们创建了BeanDefinition解析的委托类,并且解析了根元素的默认配置,那么接下来就开始具体元素的解析
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
//spring默认命名空间的元素走默认解析方式,不是默认空间的走自定义解析方式,默认空间的元素spring是知道怎么解析的,但是像我们自己定义的那些自定义元素,spring是不知道怎么解析的。
if (delegate.isDefaultNamespace(root)) {
NodeList nl = root.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
//只处理元素类型的标签,想text类型的不处理
if (node instanceof Element) {
Element ele = (Element) node;
//如果是默认命名空间元素,使用默认解析方式
if (delegate.isDefaultNamespace(ele)) {
parseDefaultElement(ele, delegate);
}
//如果是嵌入的自定义元素,走自定义元素解析方式
else {
delegate.parseCustomElement(ele);
}
}
}
}
//如果是自定义元素,走自定义元素解析方式
else {
delegate.parseCustomElement(root);
}
}
我们先来看看spring是如何解析自定义元素标签的
public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
//获取命名空间uri
String namespaceUri = getNamespaceURI(ele);
//通过命名空间uri解析出对应标签的处理器,命名空间处理器会加载当前加载器及其父加载器类路径下所有META-INF/spring.handlers
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));
}
可以看到,spring要解析自定义标签,必须从META-INF/spring.handlers找到对应的命名空间处理器,如果找不到将不会没法解析当前命名空间下的标签,为了了解自定命名空间是怎么工作的,那么我们在spring众多实现里挑选一个AopNamespaceHandler来解析一下
-
NamespaceHandler(定义初始化标签解析器,定义获取BeanDefinition以及装饰BeanDefinition的能力)
-
NamespaceHandlerSupport(提供查找具体标签解析器的能力,用户只需继承它,实现init方法,与具体的标签解析器就可以了)
下面就是AopNamespaceHandler的init方法实现
public void init() {
// In 2.0 XSD as well as in 2.1 XSD.
registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());
registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());
registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator());
// Only in 2.0 XSD: moved to context namespace as of 2.1
registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
}
以上定义了各个标签的解析器和装饰器,他们分别实现了BeanDefinitionParser和BeanDefinitionDecorator接口,BeanDefinitionParser用于将元素解析成BeanDefinition,而BeanDefinitionDecorator会在原来的BeanDefinition上进行装饰,假设我们在配置文件中配置了这样一个标签,那么最后解析这个标签的解析器是上面定义的AspectJAutoProxyBeanDefinitionParser解析器,下面是这个解析器的parse方法
public BeanDefinition parse(Element element, ParserContext parserContext) {
//注册AspectJAnnotationAutoProxyCreator
AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);
//设置includePatterns,用于过滤bean
extendBeanDefinition(element, parserContext);
return null;
}
上面这段代码中有两个参数,第一个参数是要解析的标签元素,第二个参数是以个解析上下文,为了弄清楚里面关联了什么,我们来画个类图
这个ParseContext关联了XmlReaderContext和BeanDefinitionParserDelegate,结合BeanDefinitionDocumentReader的类图,应该很好理解
了解了parse方法的参数,那么我们继续看看AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary这个方法到底做了什么
public static void registerAspectJAnnotationAutoProxyCreatorIfNecessary(
ParserContext parserContext, Element sourceElement) {
//创建AspectJAnnotationAutoProxyCreator的BeanDefinition
BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(
parserContext.getRegistry(), parserContext.extractSource(sourceElement));
//设置是否强制使用cglib代码,设置是否暴露代理对象
useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
registerComponentIfNecessary(beanDefinition, parserContext);
}
上面一段代码只是将逻辑都封装成了单独的方法去处理
public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, Object source) {
return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
}
//AnnotationAwareAspectJAutoProxyCreator.class
private static BeanDefinition registerOrEscalateApcAsRequired(Class<?> cls, BeanDefinitionRegistry registry, Object source) {
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
//判断BeanFactory中是否已经存在了这个BeanDefinition
if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
//如果已经存在了,那么判断是否是同一个,如果不是,那么就判断他们的优先级,优先级高的会替换优先级低的
BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
int requiredPriority = findPriorityForClass(cls);
if (currentPriority < requiredPriority) {
apcDefinition.setBeanClassName(cls.getName());
}
}
return null;
}
//创建BeanDefinition
RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
beanDefinition.setSource(source);
//设置属性order的值
beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
//设置BeanDefinition的角色,ROLE_INFRASTRUCTURE角色的bean不会被后置处理器处理,这个在分析bean创建的章节会提到
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
//注册这个BeanDefinition到BeanFactory中
registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
return beanDefinition;
}
//设置是否强制使用cglib代理,设置是否暴露代理对象
private static void useClassProxyingIfNecessary(BeanDefinitionRegistry registry, Element sourceElement) {
if (sourceElement != null) {
//解析proxy-target-proxy
boolean proxyTargetClass = Boolean.valueOf(sourceElement.getAttribute(PROXY_TARGET_CLASS_ATTRIBUTE));
if (proxyTargetClass) {
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
}
//解析expose-proxy-attribute
boolean exposeProxy = Boolean.valueOf(sourceElement.getAttribute(EXPOSE_PROXY_ATTRIBUTE));
if (exposeProxy) {
AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
}
}
}
上面这段代码创建了注解aop后置处理器的BeanDefinition,并给它设置了元素标签指定的属性,后置处理器是spring给予用户参与创建bean过程的一种扩展方式,我们现在创建的这个后置处理器,将会参与aop注解增强的过程。自定义标签的解析就到这里,下面本节的序列图
严格来说,前面创建Document的过程是解析默认标签与解析自定义标签共同的流程,后面部分才是解析自定标签的过程,基于此,我们自己也可以实现自己的标签
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?