首页 > 其他分享 >基于xml的Spring是如何解析@Component,@Service等注解的

基于xml的Spring是如何解析@Component,@Service等注解的

时间:2023-12-03 13:45:34浏览次数:31  
标签:xml NamespaceHandler return String Service Spring namespaceUri handler new

Spring Framework2.0开始,引入可扩展的XML编程机制,该机制要求XML Schema命名空间需要与Handler建立映射关系。该关系配置在相对于classpath下的/META-INF/spring.handlers中。

如上图所示 ContextNamespaceHandler对应<context:...> 分析的入口。

ContextNamespaceHandler 

这些NameSpaceHandler是在什么时候实例化的呢?init()方法都是在什么时候调用的呢?

在AbstractApplicationContext的refresh()方法中,我们会调用一个obtainFreshBeanFactory()方法,源码如下:

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
    refreshBeanFactory();
    ConfigurableListableBeanFactory beanFactory = getBeanFactory();
    if (logger.isDebugEnabled()) {
        logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
    }
    return beanFactory;
}

AbstractRefreshableApplicationContext#refreshBeanFactory中会调用所有AbstractRefreshableApplicationContext子类的loadBeanDefinitions()方法。

我们这里是XmlWebApplicationContext的loadBeanDefinitions方法。

一直跟踪代码,来到org.springframework.beans.factory.xml.XmlBeanDefinitionReader#doLoadBeanDefinitions,里面调用了org.springframework.beans.factory.xml.XmlBeanDefinitionReader#registerBeanDefinitions方法。

在createReaderContext(resource)中会创建NamespaceHandlerResolver接口的一个唯一实现类DefaultNamespaceHandlerResolver

回到documentReader.registerBeanDefinitions(doc, createReaderContext(resource));会调用org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#doRegisterBeanDefinitions

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
    // 1.默认命名空间的处理
    if (delegate.isDefaultNamespace(root)) {
        NodeList nl = root.getChildNodes();
        // 遍历root的子节点列表
        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)) {
                    // 1.1 默认命名空间节点的处理,例如: <bean id="test" class="" />
                    parseDefaultElement(ele, delegate);
                }
                else {
                    // 1.2 自定义命名空间节点的处理,例如:<context:component-scan/>、<aop:aspectj-autoproxy/>
                    delegate.parseCustomElement(ele);
                }
            }
        }
    } else {
        // 2.自定义命名空间的处理
        delegate.parseCustomElement(root);
    }
}

自定义命名空间节点的处理

public BeanDefinition parseCustomElement(Element ele) {
    return parseCustomElement(ele, null);
}
public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
    // 1.拿到节点ele的命名空间,例如常见的:
    // <context> 节点对应命名空间: http://www.springframework.org/schema/context
    // <aop> 节点对应命名空间: http://www.springframework.org/schema/aop
    String namespaceUri = getNamespaceURI(ele);
    // 2.拿到命名空间对应的的handler, 例如:http://www.springframework.org/schema/context 对应 ContextNameSpaceHandler
    // 2.1 getNamespaceHandlerResolver: 拿到namespaceHandlerResolver
    // 2.2 resolve: 使用namespaceHandlerResolver解析namespaceUri, 拿到namespaceUri对应的NamespaceHandler
    NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
    if (handler == null) {
        error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
        return null;
    }
    // 3.使用拿到的handler解析节点(ParserContext用于存放解析需要的一些上下文信息)
    return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}

拿到当时创建的 DefaultNamespaceHandlerResolver 对象,使用 DefaultNamespaceHandlerResolver 解析 namespaceUri,拿到对应的 handler。

ContextNamespaceHandler也就是在这时进行了init()方法初始化。

@Override
public NamespaceHandler resolve(String namespaceUri) {
    // 1.拿到配置文件的所有命名空间和对应的handler
    // 例如:"http://www.springframework.org/schema/aop" -> "org.springframework.aop.config.AopNamespaceHandler"
    Map<String, Object> handlerMappings = getHandlerMappings();
    // 2.拿到当前命名空间对应的handler (可能是handler的className,也可能是已经实例化的handler)
    Object handlerOrClassName = handlerMappings.get(namespaceUri);
    if (handlerOrClassName == null) {
        // 2.1 如果不存在namespaceUri对应的handler,则返回null
        return null;
    }
    else if (handlerOrClassName instanceof NamespaceHandler) {
        // 2.2 如果是已经实例化的handler,则直接强转返回
        return (NamespaceHandler) handlerOrClassName;
    }
    else {
        // 2.3 如果是handler的className
        String className = (String) handlerOrClassName;
        try {
            // 2.3.1 根据className,使用类加载器拿到该类
            Class<?> handlerClass = ClassUtils.forName(className, this.classLoader);
            // 2.3.2 校验是否是继承自NamespaceHandler(所有的handler都继承自NamespaceHandler)
            if (!NamespaceHandler.class.isAssignableFrom(handlerClass)) {
                throw new FatalBeanException("Class [" + className + "] for namespace [" + namespaceUri +
                                             "] does not implement the [" + NamespaceHandler.class.getName() + "] interface");
            }
            // 2.3.3 使用无参构造函数实例化handlerClass类
            NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass);
            // 2.3.4 调用handler类的初始化方法(将命名空间下的节点名和对应的解析器注册到parsers缓存中)
            namespaceHandler.init();
            // 2.3.5 将实例化的handler放到缓存,替换原来的className
            // 原来为: namespaceUri -> handler的className,会被覆盖成: namespaceUri -> 实例化的handler
            handlerMappings.put(namespaceUri, namespaceHandler);
            // 返回实例化后的handler对象
            return namespaceHandler;
        }
        catch (ClassNotFoundException ex) {
            throw new FatalBeanException("NamespaceHandler class [" + className + "] for namespace [" +
                                         namespaceUri + "] not found", ex);
        }
        catch (LinkageError err) {
            throw new FatalBeanException("Invalid NamespaceHandler class [" + className + "] for namespace [" +
                                         namespaceUri + "]: problem with handler class file or dependent class", err);
        }
    }
}

使用拿到的 handler 解析 ele 节点。

org.springframework.beans.factory.xml.NamespaceHandlerSupport#parse

public BeanDefinition parse(Element element, ParserContext parserContext) {
    // 1.findParserForElement: 给element寻找对应的BeanDefinition解析器
    // 2.使用BeanDefinition解析器解析element节点
    BeanDefinitionParser parser = findParserForElement(element, parserContext);
    return (parser != null ? parser.parse(element, parserContext) : null);
}
private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) {
    // 1.1 拿到节点的localName,例如:annotation-config、component-scan
    String localName = parserContext.getDelegate().getLocalName(element);
    // 1.2 从parsers缓存中,拿到localName对应的解析器, 例如: component-scan -> ComponentScanBeanDefinitionParser
    BeanDefinitionParser parser = this.parsers.get(localName);
    if (parser == null) {
        parserContext.getReaderContext().fatal(
            "Cannot locate BeanDefinitionParser for element [" + localName + "]", element);
    }
    return parser;
}

ComponentScanBeanDefinitionParser

在上面我们拿到具体的BeanDefinitionParser解析器后就可以调用其parse方法进行解析了。

ClassPathBeanDefinitionScanner#doScan是扫描BeanDefinition并注册的实现 。

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);
        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) {
                AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
            }
            if (checkCandidate(beanName, candidate)) {
                BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
                definitionHolder =
                    AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
                beanDefinitions.add(definitionHolder);
                registerBeanDefinition(definitionHolder, this.registry);
            }
        }
    }
    return beanDefinitions;
}

findCandidateComponents:从classPath扫描组件,并转换为备选BeanDefinition,也就是要做的解析@Component的核心方法。

findCandidateComponents在其父类ClassPathScanningCandidateComponentProvider 中。

public Set<BeanDefinition> findCandidateComponents(String basePackage) {
    if (this.componentsIndex != null && indexSupportsIncludeFilters()) {
        return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);
    }
    else {
        return scanCandidateComponents(basePackage);
    }
}

private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
    Set<BeanDefinition> candidates = new LinkedHashSet<>();
    try {
        //将package转化为ClassLoader类资源搜索路径
        String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
            resolveBasePackage(basePackage) + '/' + this.resourcePattern;
        //加载搜素路径下的资源。
        Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
        boolean traceEnabled = logger.isTraceEnabled();
        boolean debugEnabled = logger.isDebugEnabled();
        for (Resource resource : resources) {
            if (traceEnabled) {
                logger.trace("Scanning " + resource);
            }
            if (resource.isReadable()) {
                try {
                    MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
                    //判断是否是备选组件
                    if (isCandidateComponent(metadataReader)) {
                        ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
                        sbd.setResource(resource);
                        sbd.setSource(resource);
                        if (isCandidateComponent(sbd)) {
                            //添加到返回结果的list
                            candidates.add(sbd);
                        }
                    }
                }
            }
        }
    }
    return candidates;
}

ClassPathScanningCandidateComponentProvider#isCandidateComponent其源码如下:

protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
    //省略部分代码
    for (TypeFilter tf : this.includeFilters) {
        if (tf.match(metadataReader, getMetadataReaderFactory())) {
            return isConditionMatch(metadataReader);
        }
    }
    return false;
}

includeFilters由registerDefaultFilters()设置初始值,有@Component,没有@Service啊?

protected void registerDefaultFilters() {
    this.includeFilters.add(new AnnotationTypeFilter(Component.class));
    ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader();
    try {
        this.includeFilters.add(new AnnotationTypeFilter(
            ((Class<? extends Annotation>) ClassUtils.forName("javax.annotation.ManagedBean", cl)), false));
        logger.trace("JSR-250 'javax.annotation.ManagedBean' found and supported for component scanning");
    }
    catch (ClassNotFoundException ex) {
        // JSR-250 1.1 API (as included in Java EE 6) not available - simply skip.
    }
    try {
        this.includeFilters.add(new AnnotationTypeFilter(
            ((Class<? extends Annotation>) ClassUtils.forName("javax.inject.Named", cl)), false));
        logger.trace("JSR-330 'javax.inject.Named' annotation found and supported for component scanning");
    }
    catch (ClassNotFoundException ex) {
        // JSR-330 API not available - simply skip.
    }
}

Spring如何处理@Service的注解的呢?

其实@Component是任何Spring管理的组件的通用原型。@Repository、@Service和@Controller是派生自@Component。

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
// @Service 派生自@Component
@Component
public @interface Service {

    /**
    * The value may indicate a suggestion for a logical component name,
    * to be turned into a Spring bean in case of an autodetected component.
    * @return the suggested component name, if any (or empty String otherwise)
    */
    @AliasFor(annotation = Component.class)
    String value() default "";

}

@Component是@Service的元注解,在读取@Service,也读取了它的元注解,并将@Service作为@Component处理。

AnnotationAttributesReadingVisitor#visitEnd()内部方法recursivelyCollectMetaAnnotations 递归的读取注解,与注解的元注解(读@Service,再读元注解@Component),并设置到metaAnnotationMap。

final class AnnotationAttributesReadingVisitor extends RecursiveAnnotationAttributesVisitor {
    private final MultiValueMap<String, AnnotationAttributes> attributesMap;
    private final Map<String, Set<String>> metaAnnotationMap;

    public AnnotationAttributesReadingVisitor(String annotationType, MultiValueMap<String, AnnotationAttributes> attributesMap, Map<String, Set<String>> metaAnnotationMap, @Nullable ClassLoader classLoader) {
        super(annotationType, new AnnotationAttributes(annotationType, classLoader), classLoader);
        this.attributesMap = attributesMap;
        this.metaAnnotationMap = metaAnnotationMap;
    }

    public void visitEnd() {
        super.visitEnd();
        Class<?> annotationClass = this.attributes.annotationType();
        if (annotationClass != null) {
            List<AnnotationAttributes> attributeList = (List)this.attributesMap.get(this.annotationType);
            if (attributeList == null) {
                this.attributesMap.add(this.annotationType, this.attributes);
            } else {
                attributeList.add(0, this.attributes);
            }

            Set<Annotation> visited = new LinkedHashSet();
            Annotation[] metaAnnotations = AnnotationUtils.getAnnotations(annotationClass);
            if (!ObjectUtils.isEmpty(metaAnnotations)) {
                Annotation[] var5 = metaAnnotations;
                int var6 = metaAnnotations.length;

                for(int var7 = 0; var7 < var6; ++var7) {
                    Annotation metaAnnotation = var5[var7];
                    if (!AnnotationUtils.isInJavaLangAnnotationPackage(metaAnnotation)) {
                        this.recursivelyCollectMetaAnnotations(visited, metaAnnotation);
                    }
                }
            }

            Set<String> metaAnnotationTypeNames = new LinkedHashSet(visited.size());
            Iterator var10 = visited.iterator();

            while(var10.hasNext()) {
                Annotation ann = (Annotation)var10.next();
                metaAnnotationTypeNames.add(ann.annotationType().getName());
            }

            this.metaAnnotationMap.put(annotationClass.getName(), metaAnnotationTypeNames);
        }

    }

    private void recursivelyCollectMetaAnnotations(Set<Annotation> visited, Annotation annotation) {
        Class<? extends Annotation> annotationType = annotation.annotationType();
        String annotationName = annotationType.getName();
        if (!AnnotationUtils.isInJavaLangAnnotationPackage(annotationName) && visited.add(annotation)) {
            try {
                if (Modifier.isPublic(annotationType.getModifiers())) {
                    this.attributesMap.add(annotationName, AnnotationUtils.getAnnotationAttributes(annotation, false, true));
                }

                Annotation[] var5 = annotationType.getAnnotations();
                int var6 = var5.length;

                for(int var7 = 0; var7 < var6; ++var7) {
                    Annotation metaMetaAnnotation = var5[var7];
                    this.recursivelyCollectMetaAnnotations(visited, metaMetaAnnotation);
                }
            } catch (Throwable var9) {
                if (this.logger.isDebugEnabled()) {
                    this.logger.debug("Failed to introspect meta-annotations on [" + annotation + "]: " + var9);
                }
            }
        }

    }
}

 

标签:xml,NamespaceHandler,return,String,Service,Spring,namespaceUri,handler,new
From: https://www.cnblogs.com/xfeiyun/p/17871713.html

相关文章

  • Github Actions - Creating PostgreSQL service containers
     #Servicecontainerstorunwith`container-job`services:#Labelusedtoaccesstheservicecontainerpostgres:#DockerHubimageimage:postgres#Providethepasswordforpostgresenv:......
  • SpringBoot如何使用@Scheduled创建定时任务?
    前言随着软件业务日益复杂,有时候需要创建一些定时任务以满足特定业务需求。在Java编程语言中,使用Spring框架可以轻松地创建出定时任务。SpringBoot作为Spring框架中的一员,在创建定时任务方面也提供了便利性和灵活性。本文将介绍SpringBoot如何使用@Scheduled创建定时任务。摘要......
  • 整合springboot+mybatisplus+mysql+swagger,开发后端接口
    一、版本1.springboot:2.7.82.mybatisplus:3.5.3.13.mysql:8.04.swagger:3.0二、建立数据表(省略表结构)三、新建maven工程及springboot项目1.完成后的项目目录:2.maven工程的pom文件引入依赖:点击查看代码<?xmlversion="1.0"encoding="UTF-8"?><projectxmlns="http......
  • Spring是如何解析自定义的xml标签
    SpringSPISpring借鉴了JavaSPI思想来解析各种标签,我们称之为SpringSPI。SpringSPI沿用了JavaSPI的设计思想,但在实现上和JavaSPI及DubboSPI也存在差异,Spring通过spring.handlers和spring.factories两种方式实现SPI机制,可以在不修改Spring源码的前提下,做到对Spring框架的扩......
  • SAP 标准 OData 服务 CATALOGSERVICE;v=2 的作用介绍
    sap/opu/odata/IWFND/CATALOGSERVICE;v=2是SAPNetWeaverGateway的标准OData服务之一,用于管理和展示服务目录。通过该服务,用户可以获取有关已注册的服务的详细信息,包括服务的元数据、技术细节和相关文档。以下是对该服务的详细介绍,包括其作用和使用示例。作用服务目录管......
  • 什么是 SAP XML annotation language server
    来自SAP官方的解释:TheXMLannotationlanguageserveraccelerateshowyouworkwithannotationsinthecodeeditor.Context-sensitivecodecompletiondisplayssuggestionsthatarerelevanttowhereyouareintheannotationfileforyourapp.Asyoutypea......
  • Spring Boot单元测试
    1.概述所谓单元测试就是对功能最小粒度的测试,落实到JAVA中就是对单个方法的测试。对单个方法的测试用junit即可,关于junit作者另一位篇文章中有详细介绍,感兴趣的小伙伴可以去看看:详解junit-CSDN博客junit可以完成单个方法的测试,但是对于Spring体系下的web应用的单元测试是无能为力的......
  • Spring Boot 3.2.0 试用CRaC,启动速度提升3到10倍
    CRaC(CoordinatedRestoreatCheckpoint)。CRaC是OpenJDK项目,能够把运行中的JVM,将其状态(包括您的应用程序)存储到磁盘中。然后,在另一个时间点,您可以从保存的检查点将JVM恢复到内存中。通过这种方式,可以启动一个应用程序,预热它,并创建一个检查点。从保存的检查点恢复到内存主要......
  • Spring 中的 URL 处理工具类 UriComponentsBuilder
    UriComponentsBuilder是SpringFramework中的一个用于构建URI(UniformResourceIdentifier)和URL(UniformResourceLocator)的实用程序类。它提供了一种简单的方式来构建包含各种部分(如协议、主机、路径、查询参数等)的URI和URL,并支持对这些部分进行修改、替换和合并等操作。以下是......
  • SpringBoot自定义注解导出Excel
    先定义一个注解importjava.lang.annotation.Retention;importjava.lang.annotation.RetentionPolicy;@Retention(RetentionPolicy.RUNTIME)public@interfaceExcelHander{Stringvalue()default"";StringlinkFiled()default"";Cel......