Preface

Before we talked about custom SPI integrates with spring , today we will talk about how to inject spi objects into the spring container through a custom label

Realization routine

1. Custom xsd

example :

<?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: If you are not familiar with xsd, you can refer to the following link

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

2. Custom parse the BeanDefinitionParser parser

example:

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. Define the NamespaceHandler implementation class to handle custom label handlers

example:

public class SpiNamespaceHandler extends NamespaceHandlerSupport {

    @Override
    public void init() {
        registerBeanDefinitionParser("scan", new AnnotationBeanDefinitionParser(SpiAnnotationPostProcessor.class));
    }
}
4. Write the location of the processor and label to spring.handlers and spring.schemas

example:

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

Note: spring.handlers, spring.schemas need to be placed under the resource/META-INF directory

Sample demo

1. Configure 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>

2. Import xml on the startup class
@SpringBootApplication
@ImportResource(locations = "classpath:/spi.xml")
public class SpiTestXmlApplication {


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


}
3. Verify whether the SPI is injected into the spring container
 @Override
    public void run(ApplicationArguments args) throws Exception {

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

The console input is as follows

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

Description has been imported into the spring container

Summarize

Since the introduction of annotation drivers in spring 3+, xml is rarely used in new projects, but if it is an old project, if you want to inject custom tags into spring, you can use the method in this article.

The routine is as follows

  • 1. Custom xsd
  • 2. Custom parse the BeanDefinitionParser parser
  • 3. Define the NamespaceHandler implementation class to handle custom label handlers
  • 4. Write the location of the processor and label to spring.handlers and spring.schemas

The implementation of this article is relatively simple. If you want to use it in depth, I recommend looking at the dubbo custom spring tag.

demo link

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


linyb极客之路
336 声望193 粉丝