聊聊自定义SPI如何使用自定义标签注入到spring容器中

Posted Linyb极客之路

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了聊聊自定义SPI如何使用自定义标签注入到spring容器中相关的知识,希望对你有一定的参考价值。

点击上方蓝字关注我们


01

前言


之前我们聊过自定义的SPI如何与spring进行整合,今天我们就来聊下如何通过自定义标签将spi对象注入到spring容器中


02

实现套路



1、自定义xsd


示例


<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
            xmlns:beans="http://www.springframework.org/schema/beans"
            xmlns:tool="http://www.springframework.org/schema/tool"
            xmlns="http://lybgeek.github.com/schema/spi"
            targetNamespace="http://lybgeek.github.com/schema/spi">


    <xsd:import namespace="http://www.w3.org/XML/1998/namespace"/>
    <xsd:import namespace="http://www.springframework.org/schema/beans"
                schemaLocation="http://www.springframework.org/schema/beans/spring-beans.xsd"/>

    <xsd:import namespace="http://www.springframework.org/schema/tool"/>

    <xsd:annotation>
        <xsd:documentation>
            <![CDATA[ Namespace support for spi services ]]></xsd:documentation>
    </xsd:annotation>


    <xsd:complexType name="scanType">
        <xsd:attribute name="id" type="xsd:ID">
            <xsd:annotation>
                <xsd:documentation><![CDATA[ The unique identifier for a bean. ]]></xsd:documentation>
            </xsd:annotation>
        </xsd:attribute>
        <xsd:attribute name="basePackages" type="xsd:string" use="required">
            <xsd:annotation>
                <xsd:documentation><![CDATA[ Specify the spi package name to scan, multiple scan packages are separated by commas ]]></xsd:documentation>
            </xsd:annotation>
        </xsd:attribute>
    </xsd:complexType>

    <xsd:complexType name="interceptorType">
        <xsd:attribute name="id" type="xsd:ID">
            <xsd:annotation>
                <xsd:documentation><![CDATA[ The unique identifier for a bean. ]]></xsd:documentation>
            </xsd:annotation>
        </xsd:attribute>
        <xsd:attribute name="class" type="xsd:string" use="required">
            <xsd:annotation>
                <xsd:documentation><![CDATA[ Interceptor class name]]></xsd:documentation>
            </xsd:annotation>
        </xsd:attribute>
    </xsd:complexType>

    <xsd:complexType name="interceptorChainType">
        <xsd:choice>
            <xsd:element ref="interceptor" minOccurs="1" maxOccurs="unbounded"/>
        </xsd:choice>
    </xsd:complexType>


    <xsd:element name="scan" type="scanType">
        <xsd:annotation>
            <xsd:documentation><![CDATA[ The scan config ]]></xsd:documentation>
        </xsd:annotation>
    </xsd:element>

    <xsd:element name="interceptor" type="interceptorType">
        <xsd:annotation>
            <xsd:documentation><![CDATA[ The interceptor config ]]></xsd:documentation>
        </xsd:annotation>
    </xsd:element>

    <xsd:element name="interceptorChain" type="interceptorChainType">
        <xsd:annotation>
            <xsd:documentation><![CDATA[ The interceptorChainType config ]]></xsd:documentation>
        </xsd:annotation>
    </xsd:element>

</xsd:schema>


ps: 如果对xsd不熟悉的朋友,可以参考如下链接


https://www.w3school.com.cn/schema/index.asp


2、自定义解析BeanDefinitionParser解析器


示例:


public class AnnotationBeanDefinitionParser implements BeanDefinitionParser 

    private final Class<?> beanClass;

    public AnnotationBeanDefinitionParser(Class<?> beanClass) 
        this.beanClass = beanClass;
    

    @Override
    public BeanDefinition parse(Element element, ParserContext parserContext) 

        String packageToScan = element.getAttribute("basePackages");
        String[] packagesToScan = trimArrayElements(commaDelimitedListToStringArray(packageToScan));

        RootBeanDefinition beanDefinition = new RootBeanDefinition();
        beanDefinition.setBeanClass(beanClass);
        beanDefinition.setLazyInit(false);
        beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0,packagesToScan);
        String beanName = BeanUtils.generateBeanName(element,"id",parserContext,beanClass.getName());
        parserContext.getRegistry().registerBeanDefinition(beanName,beanDefinition);

        return beanDefinition;
    



3、定义NamespaceHandler实现类处理自定义标签的处理器


示例:


public class SpiNamespaceHandler extends NamespaceHandlerSupport 

    @Override
    public void init() 
        registerBeanDefinitionParser("scan", new AnnotationBeanDefinitionParser(SpiAnnotationPostProcessor.class));
    


4、将写入处理器、标签的位置写入spring.handlers、spring.schemas中


示例:

spring.handlers

http\\://lybgeek.github.com/schema/spi=com.github.lybgeek.spring.schema.SpiNamespaceHandler

spring.schemas

http\\://lybgeek.github.com/schema/spi/spi.xsd=META-INF/spi/spi.xsd


注: spring.handlers、spring.schemas需放置在resource/META-INF目录底下


03

示例演示



01

配置xml


<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:spi="http://lybgeek.github.com/schema/spi"
       xmlns="http://www.springframework.org/schema/beans"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
       http://lybgeek.github.com/schema/spi http://lybgeek.github.com/schema/spi/spi.xsd">

    <spi:scan basePackages="com.github.lybgeek"></spi:scan>


02

在启动类上导入xml


@SpringBootApplication
@ImportResource(locations = "classpath:/spi.xml")
public class SpiTestXmlApplication


    public static void main(String[] args) throws Exception
        SpringApplication.run(SpiTestXmlApplication.class);
    



03

验证SPI是否注入spring容器


@Override
    public void run(ApplicationArguments args) throws Exception

        applicationContext.getBeansOfType(SpringSqlDialect.class)
                .forEach((beanName,bean) -> System.out.println(beanName + "-->" + bean));
    


springmysqlDialect-->com.github.lybgeek.dialect.mysql.SpringMysqlDialect@73041b7d
mysql-hello-->com.github.lybgeek.dialect.mysql.SpringMysqlDialect@574059d5
springOracleDialect-->com.github.lybgeek.dialect.oracle.SpringOracleDialect@4a50d04a


说明已经导入到spring容器中


04

总结


自从spring3+开始引入注解驱动后,在新项目基本上很少会使用xml,但如果是一些老旧的项目,大家如果想实现自定义标签注入到spring,就可以使用本文的方式。

套路就是如下

  •     1、自定义xsd

  •   2、自定义解析BeanDefinitionParser解析器

  •   3、定义NamespaceHandler实现类处理自定义标签的处理器

  •   4、将写入处理器、标签的位置写入spring.handlers、spring.schemas中

  • 本文的实现也是相对简单,如果想深入使用,推荐看看dubbo自定义spring标签


    05

    demo链接



    https://github.com/lyb-geek/springboot-learning/tree/master/springboot-spi-enhance/springboot-spi-framework-spring



    Spring源码解析-自定义标签解析和SPI机制-3

    1. 涉及SPI机制地方

    1. 自定义标签:例如:context aop 等等都是自定义标签,需要利用SPI机制
    2. 默认标签中的自定义元素加载也涉及到SPI机制

    2. SPI机制:

    SPI就是一个服务的扩展机制,可以把接口的实现类配置到META-INF元数据区,框架启动时加载到缓存,最初的版本是jdk中实现的,后来在spring、springboot、dubbo中都有相应的使用。

    3. JDK的SPI机制:

    META-INF下创建services目录,然后以接口全限定名为文件名,将实现类的全限定名放进去,这样运行程序时,会加载实现类的名称进jvm,调用的时候会调用newInstance()方法实例化对象。

    示例:

    • 创建一个IAnimal接口:
    package com.hello.spi;
     
    public interface IAnimal {
        void sing();
    }
    • 创建两个实现类:
    package com.hello.spi;
     
    public class Cat implements IAnimal {
        @Override
        public void sing() {
            System.out.println("cat sing......");
        }
    }
    package com.hello.spi;
     
    public class Dog implements IAnimal {
        @Override
        public void sing() {
            System.out.println("dog sing......");
        }
    }
    //全路径需和类路径保持一致
    resource\\META-INF.services\\com.hello.spi.IAnimal
    //此路径文件下有
    com.hello.spi.Cat
    com.hello.spi.Dog
    • 测试代码:
    public class TestSPI {
        public static void main(String[] args) {
            ServiceLoader<IAnimal> animals = ServiceLoader.load(IAnimal.class);
            for (Iterator<IAnimal> iter = animals.iterator();iter.hasNext();) {
                IAnimal animal = iter.next();
                animal.sing();
            }
        }
    }

    5. spring的spi机制:

    • 获取spring中所有jar包里面的"META-INF/spring.handlers"文件,并且建立映射关系
    • spring的类DefaultNamespaceHandlerResolver这个类,会懒加载spring.handler文件内配置的实现类进内存
    • 读取META-INF/spring.handlers目录下的实现类进jvm
    • 然后缓存到handlerMappings,等待后面使用
    • 这个是spring-context工程下spring.handlers文件内容: key为命名空间url、value为类的全限定名,加载完成后会缓存到handlerMappings中
    //此方法上一篇中有提到
        private Map<String, Object> getHandlerMappings() {
           Map<String, Object> handlerMappings = this.handlerMappings;
           if (handlerMappings == null) {
              synchronized (this) {
                 handlerMappings = this.handlerMappings;
                 if (handlerMappings == null) {
                    if (logger.isTraceEnabled()) {
                       logger.trace("Loading NamespaceHandler mappings from [" + this.handlerMappingsLocation + "]");
                    }
                    try {
                       //加载"META-INF/spring.handlers"文件过程
                       Properties mappings =
                             PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader);
                       if (logger.isTraceEnabled()) {
                          logger.trace("Loaded NamespaceHandler mappings: " + mappings);
                       }
        
                       //所有"META-INF/spring.handlers"文件里面的内容建立映射关系
                       handlerMappings = new ConcurrentHashMap<>(mappings.size());
                       CollectionUtils.mergePropertiesIntoMap(mappings, handlerMappings);
                       this.handlerMappings = handlerMappings;
                    }
                    catch (IOException ex) {
                       throw new IllegalStateException(
                             "Unable to load NamespaceHandler mappings from location [" + this.handlerMappingsLocation + "]", ex);
                    }
                 }
              }
           }
           return handlerMappings;
        }

    6. 自定义标签的解析

    6.1 解析自定义元素

        public BeanDefinition parseCustomElement(Element ele) {
            return parseCustomElement(ele, null);
        }
    
        public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
            // 解析到自定义元素的命名空间,例如:context元素命名空间  http://www.springframework.org/schema/context
            String namespaceUri = getNamespaceURI(ele);
            if (namespaceUri == null) {
                return null;
            }
            // 根据url找命名空间handler
            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));
        }

    6.2 寻找命名空间对象(此方法上一章有用到)

        public NamespaceHandler resolve(String namespaceUri) {
            //获取spring中所有jar包里面的 "META-INF/spring.handlers"文件,并且建立映射关系
            Map<String, Object> handlerMappings = getHandlerMappings();
    
            //根据namespaceUri:http://www.springframework.org/schema/p,获取到这个命名空间的处理类
            // 根据url找到对应的全限定类名
            Object handlerOrClassName = handlerMappings.get(namespaceUri);
            if (handlerOrClassName == null) {
                return null;
            }
            //如果时对象直接返回,说明不是第一次使用
            else if (handlerOrClassName instanceof NamespaceHandler) {
                return (NamespaceHandler) handlerOrClassName;
            }
            // 首次调用,会走下述逻辑
            // 1.根据类名加载此类,创建class对象
            // 2.根据class反射创建对象
            // 3.初始化init
            // 4.放入缓存
            // 5.返回这个对象
            else {
                String className = (String) handlerOrClassName;
                try {
                    //反射出来类
                    Class<?> handlerClass = ClassUtils.forName(className, this.classLoader);
                    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);
    
                    //调用处理类的init方法,在init方法中完成标签元素解析类的注册***重要
                    namespaceHandler.init();
                    handlerMappings.put(namespaceUri, namespaceHandler);
                    return namespaceHandler;
                }
                // catch略......
            }
        }

    6.3 初始化,把属性和解析器缓存映射,主要的处理缓存逻辑都在父类NamespaceHandlerSupport中, 这样初始化之后,这个NamespaceHandler就包含了众多解析器。

        public class ContextNamespaceHandler extends NamespaceHandlerSupport {
        
            @Override
            public void init() {
                registerBeanDefinitionParser("property-placeholder", new PropertyPlaceholderBeanDefinitionParser());
                registerBeanDefinitionParser("property-override", new PropertyOverrideBeanDefinitionParser());
                registerBeanDefinitionParser("annotation-config", new AnnotationConfigBeanDefinitionParser());
                registerBeanDefinitionParser("component-scan", new ComponentScanBeanDefinitionParser());
                registerBeanDefinitionParser("load-time-weaver", new LoadTimeWeaverBeanDefinitionParser());
                registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
                registerBeanDefinitionParser("mbean-export", new MBeanExportBeanDefinitionParser());
                registerBeanDefinitionParser("mbean-server", new MBeanServerBeanDefinitionParser());
            }
        
        }
        
        //NamespaceHandlerSupport类中
        protected final void registerBeanDefinitionParser(String elementName, BeanDefinitionParser parser) {
            this.parsers.put(elementName, parser);
        }

    6.4 跳回到6.1拿到NamespaceHandler,开始解析

        public BeanDefinition parse(Element element, ParserContext parserContext) {
            BeanDefinitionParser parser = findParserForElement(element, parserContext);
            return (parser != null ? parser.parse(element, parserContext) : null);
        }
            
        private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) {
            String localName = parserContext.getDelegate().getLocalName(element);
            //从缓存中拿到component-scan的解析器
            BeanDefinitionParser parser = this.parsers.get(localName);
            if (parser == null) {
                parserContext.getReaderContext().fatal(
                        "Cannot locate BeanDefinitionParser for element [" + localName + "]", element);
            }
            return parser;
        }

    6.5 拿到解析器,跳转至ComponentScanBeanDefinitionParser,开始解析

        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);
            Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);
            registerComponents(parserContext.getReaderContext(), beanDefinitions, element);
        
            return null;
        }

    6.6 创建一个扫描器

        protected ClassPathBeanDefinitionScanner configureScanner(ParserContext parserContext, Element element) {
            //使用默认过滤器
            boolean useDefaultFilters = true;
            if (element.hasAttribute(USE_DEFAULT_FILTERS_ATTRIBUTE)) {
                useDefaultFilters = Boolean.valueOf(element.getAttribute(USE_DEFAULT_FILTERS_ATTRIBUTE));
            }
        
            // Delegate bean definition registration to scanner class.
            //创建扫描器
            ClassPathBeanDefinitionScanner scanner = createScanner(parserContext.getReaderContext(), useDefaultFilters);
            scanner.setBeanDefinitionDefaults(parserContext.getDelegate().getBeanDefinitionDefaults());
            scanner.setAutowireCandidatePatterns(parserContext.getDelegate().getAutowireCandidatePatterns());
        
            if (element.hasAttribute(RESOURCE_PATTERN_ATTRIBUTE)) {
                scanner.setResourcePattern(element.getAttribute(RESOURCE_PATTERN_ATTRIBUTE));
            }
        
            try {
                parseBeanNameGenerator(element, scanner);
            }
            catch (Exception ex) {
                parserContext.getReaderContext().error(ex.getMessage(), parserContext.extractSource(element), ex.getCause());
            }
        
            try {
                parseScope(element, scanner);
            }
            catch (Exception ex) {
                parserContext.getReaderContext().error(ex.getMessage(), parserContext.extractSource(element), ex.getCause());
            }
        
            parseTypeFilters(element, scanner, parserContext);
        
            return scanner;
        }
        protected ClassPathBeanDefinitionScanner createScanner(XmlReaderContext readerContext, boolean useDefaultFilters) {
            return new ClassPathBeanDefinitionScanner(readerContext.getRegistry(), useDefaultFilters,
                    readerContext.getEnvironment(), readerContext.getResourceLoader());
        }
        
        public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters,
                Environment environment, @Nullable ResourceLoader resourceLoader) {
        
            Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
            this.registry = registry;
        
            if (useDefaultFilters) {
                //使用默认过滤器,注册默认过滤器
                registerDefaultFilters();
            }
            setEnvironment(environment);
            setResourceLoader(resourceLoader);
        }

    6.7 注册过滤器,支持注解扫描

        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.
            }
        }

    6.8 跳回到6.5,创建扫描器完成后,就可以扫描basePackages包了

        Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);
    
        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) {
                // 寻找符合条件的类,有Component注解的
                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);
                        //把BeanDefinition注册到spring的缓存中
                        registerBeanDefinition(definitionHolder, this.registry);
                    }
                }
            }
            return beanDefinitions;
        }

    6.9 寻找符合条件的类

        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 {
                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)) {
                                    if (debugEnabled) {
                                        logger.debug("Identified candidate component class: " + resource);
                                    }
                                    candidates.add(sbd);
                                }
                                else {
                                    if (debugEnabled) {
                                        logger.debug("Ignored because not a concrete top-level class: " + resource);
                                    }
                                }
                            }
                            else {
                                if (traceEnabled) {
                                    logger.trace("Ignored because not matching any filter: " + resource);
                                }
                            }
                        }
                        catch (Throwable ex) {
                            throw new BeanDefinitionStoreException(
                                    "Failed to read candidate component class: " + resource, ex);
                        }
                    }
                    else {
                        if (traceEnabled) {
                            logger.trace("Ignored because not readable: " + resource);
                        }
                    }
                }
            }
            catch (IOException ex) {
                throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);
            }
            return candidates;
        }
        protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
            //排除掉的组件
            for (TypeFilter tf : this.excludeFilters) {
                if (tf.match(metadataReader, getMetadataReaderFactory())) {
                    return false;
                }
            }
            //包含的组件
            for (TypeFilter tf : this.includeFilters) {
                if (tf.match(metadataReader, getMetadataReaderFactory())) {
                    return isConditionMatch(metadataReader);
                }
            }
            return false;
        }

    6.10 跳回6.7查看注册器过滤器类型AnnotationTypeFilter,其父类AbstractTypeHierarchyTraversingFilter

        public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)
                throws IOException {
    
            // This method optimizes avoiding unnecessary creation of ClassReaders
            // as well as visiting over those readers.
            if (matchSelf(metadataReader)) {
                return true;
            }
            ClassMetadata metadata = metadataReader.getClassMetadata();
            if (matchClassName(metadata.getClassName())) {
                return true;
            }
    
            if (this.considerInherited) {
                String superClassName = metadata.getSuperClassName();
                if (superClassName != null) {
                    // Optimization to avoid creating ClassReader for super class.
                    Boolean superClassMatch = matchSuperClass(superClassName);
                    if (superClassMatch != null) {
                        if (superClassMatch.booleanValue()) {
                            return true;
                        }
                    }
                    else {
                        // Need to read super class to determine a match...
                        try {
                            if (match(metadata.getSuperClassName(), metadataReaderFactory)) {
                                return true;
                            }
                        }
                        catch (IOException ex) {
                            logger.debug("Could not read super class [" + metadata.getSuperClassName() +
                                    "] of type-filtered class [" + metadata.getClassName() + "]");
                        }
                    }
                }
            }
    
            if (this.considerInterfaces) {
                for (String ifc : metadata.getInterfaceNames()) {
                    // Optimization to avoid creating ClassReader for super class
                    Boolean interfaceMatch = matchInterface(ifc);
                    if (interfaceMatch != null) {
                        if (interfaceMatch.booleanValue()) {
                            return true;
                        }
                    }
                    else {
                        // Need to read interface to determine a match...
                        try {
                            if (match(ifc, metadataReaderFactory)) {
                                return true;
                            }
                        }
                        catch (IOException ex) {
                            logger.debug("Could not read interface [" + ifc + "] for type-filtered class [" +
                                    metadata.getClassName() + "]");
                        }
                    }
                }
            }
    
            return false;
        }

    6.11 跳转回子类进行判断

        protected boolean matchSelf(MetadataReader metadataReader) {
            AnnotationMetadata metadata = metadataReader.getAnnotationMetadata();
            return metadata.hasAnnotation(this.annotationType.getName()) ||
                    (this.considerMetaAnnotations && metadata.hasMetaAnnotation(this.annotationType.getName()));
        }

    6.12 跳转回6.9,如果发现这个类上面有Component注解信息,那么就符合条件。对于符合条件的,就会创建一个beanDefinition对象,然后将元数据信息封装进去,并放入容器中,最后返回

    6.13 跳回6.8,拿到candidates后,就会遍历处理,返回beanDefinition集合,结合6.5,然后将beanDefinition集合注册到spring容器中,这样这个context的自定义标签解析完毕。

    <!--配置扫描路径样例-->
    <context:component-scan base-package="com.hello" />

    总结

    • 上面是以context:component-scan这个自定义标签为例,分析了解析的流程,大概流程如下:
    • 获取元素命名空间url ,nameSpaceUrl
    • 根据namespaceUrl找到nameSpaceHandler,这个使用到了spi懒加载机制(初次获取会反射创建handler对象,然后缓存)
    • 创建handler后,会init初始化,初始化的过程中会缓存各种解析器
    • 根据元素的localname,查询到解析器,然后调用解析器的parse方法开始解析
    • 无论是默认标签还是自定义标签,它们最终都是生成beanDefinition对象,然后注册到beanDefinitionMaps中缓存。

    以上是关于聊聊自定义SPI如何使用自定义标签注入到spring容器中的主要内容,如果未能解决你的问题,请参考以下文章

    自定义SPI使用JDK动态代理遇到UndeclaredThrowableException异常排查

    聊聊Dubbo核心源码-SPI扩展

    你会用Spring自定义Xml标签吗

    Spring 自定义标签配置

    java web如何防止html,js注入

    dubbo源码系列3——dubbo自定义标签解析