A core part of Spring is inversion of control, and the objects to be controlled are the various beans.

Although most teams now use Spring Boot directly and few use Spring MVC, the base is still Spring, with more of an ANNOTated XML configuration.

If you have used XML configuration, do you know how to implement tag configurations such as Context: Component-scan, aop: Aspectj-AutoProxy? Knowing this will help you to learn more about Spring.

Come on, here we go!

Spring MVC provides a mechanism for extending XML to write custom XML beans. For example, the Dubbo framework uses this mechanism to implement a number of Dubbo beans. Such as Dubbo: Application, Dubbo: Registry, etc., just install the standard extension to implement the configuration.

What is the point of extending custom beans

Assuming we’re going to use an open source framework or API, we want two things:

1. Ease of use, that is, the configuration is simple and the fewer places to configure the better

2. Encapsulation, simple call, that is, the higher the encapsulation, the better, less exposure to the underlying implementation

Based on the above, let’s say we want to implement a custom feature that we can do with existing Spring configuration items, but that may require a lot of configuration and may require code assistance. As a result, the logic is scattered and is not easy to maintain.

So we extended The Spring configuration to encapsulate some of the custom complex functionality and minimize the configuration.

Steps to implement custom extensions

This example is a simple demonstration that implements a Hacker Bean with configurable parameters and then provides a toString() method to input parameter information. Our final bean configuration is as follows:

<kite:hacker id=”hacker” name=”moon” language=”english” age=”18″ isHide=”true”/>

Use Spring’s built-in configuration for comparison, for example:

<context:component-scan base-package=”com.ebanghu”></context:component-scan>

1, implement a custom bean class named Hacker and override the toString() method with the property name:

<pre language="typescript" code_block="true">package kite.lab.spring.config;

/**
 * Hacker
 * @author fengzheng
 */
public class Hacker {
    private String name;
    private String age;
    private String language;
    private boolean isHide;

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }

    public String getAge() {
        return age;
    }

    public void setAge(String age) {
        this.age = age;
    }

    public String getLanguage() {
        return language;
    }

    public void setLanguage(String language) {
        this.language = language;
    }

    public boolean isHide() {
        return isHide;
    }

    public void setHide(boolean hide) {
        isHide = hide;
    }

    @Override
    public String toString() {
        StringBuilder builder = new StringBuilder();
        builder.append("======================\n");
        builder.append(String.format("hacker's name is :%s \n", this.getName()));
        builder.append(String.format("hacker's age is :%s \n", this.getAge()));
        builder.append(String.format("hacker's language is :%s \n", this.getLanguage()));
        builder.append(String.format("hacker's status is :%s \n", this.isHide()));
        builder.append("======================\n");
        returnbuilder.toString(); }}Copy the code

XSD: XSD: XSD: XSD: XSD: XSD: XSD: XSD: XSD: XSD: XSD: XSD: XSD: XSD: XSD This file is an XML structure description of the entity class you just created. It looks like this:

<pre language="typescript" code_block="true"> <? xml version="1.0" encoding="UTF-8"? > <xsd:schema xmlns="http://code.fengzheng.com/schema/kite"
            xmlns:xsd="http://www.w3.org/2001/XMLSchema"
            xmlns:beans="http://www.springframework.org/schema/beans"
            targetNamespace="http://code.fengzheng.com/schema/kite"
            elementFormDefault="qualified"
            attributeFormDefault="unqualified">
    <xsd:import namespace="http://www.springframework.org/schema/beans"/>
    <xsd:complexType name="hackType">
        <xsd:complexContent>
            <xsd:extension base="beans:identifiedType">
                <xsd:attribute name="name" type="xsd:string" use="required"> <xsd:annotation> <xsd:documentation> <! [CDATA[ The name of hacker ]]> </xsd:documentation> </xsd:annotation> </xsd:attribute> <xsd:attribute name="age" type="xsd:int" use="optional" default="0"> <xsd:annotation> <xsd:documentation><! [CDATA[ The age of hacker. ]]></xsd:documentation> </xsd:annotation> </xsd:attribute> <xsd:attribute name="language" type="xsd:string" use="optional"> <xsd:annotation> <xsd:documentation><! [CDATA[ The language of hacker. ]]></xsd:documentation> </xsd:annotation> </xsd:attribute> <xsd:attribute name="isHide" type="xsd:boolean" use="optional"> <xsd:annotation> <xsd:documentation><! [CDATA[ The status of hacker. ]]></xsd:documentation> </xsd:annotation> </xsd:attribute> </xsd:extension> </xsd:complexContent> </xsd:complexType> <xsd:element name="hacker" type="hackType"> <xsd:annotation> <xsd:documentation><! [CDATA[ The hacker config ]]></xsd:documentation> </xsd:annotation> </xsd:element> </xsd:schema></pre> </pre>Copy the code

Notice the top

xmlns=”code.fengzheng.com/schema/kite

and

targetNamespace=”code.fengzheng.com/schema/kite”

I’ll need something later.

3, implement NamespaceHandler class, the code is as follows:

<pre language="typescript" code_block="true">package kite.lab.spring.config;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.xml.NamespaceHandlerSupport;

/**
 * HackNamespaceHandler
 * @author fengzheng
 */
public class HackNamespaceHandler extends NamespaceHandlerSupport {

    private static final Logger logger = LoggerFactory.getLogger(HackNamespaceHandler.class);

    @Override
    public void init() {
        logger.info("Execute HackNamespaceHandler's init method.");
        registerBeanDefinitionParser("hacker",new HackBeanDefinitionParser(Hacker.class));
        logger.info("Registered 'hacker' to define converter successfully");
    }
}
</pre>
Copy the code

Such a function is very simple, it is inherited NamespaceHandlerSupport class, and reload the init method, call registerBeanDefinitionParser method, The first parameter hacker is the name we will use later in our Spring configuration file, kite:hacker hacker here; The second parameter is the next step.

BeanDefinitionParser class BeanDefinitionParser class BeanDefinitionParser class BeanDefinitionParser

<pre language="typescript" code_block="true">package kite.lab.spring.config; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.MutablePropertyValues; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.beans.factory.xml.BeanDefinitionParser; import org.springframework.beans.factory.xml.ParserContext; import org.w3c.dom.Element; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Parameter; /** * HackBeanDefinitionParser * * @author fengzheng */ public class HackBeanDefinitionParser implements BeanDefinitionParser { private static final Logger logger = LoggerFactory.getLogger(HackBeanDefinitionParser.class); private final Class<? > beanClass; public HackBeanDefinitionParser(Class<? > beanClass) { this.beanClass = beanClass; } @Override public BeanDefinition parse(Element element, ParserContext parserContext) { logger.info(Go to the parse method of HckBeanDefinitionParser);
        try {
            String id = element.getAttribute("id");
            RootBeanDefinition rootBeanDefinition = new RootBeanDefinition();
            rootBeanDefinition.setBeanClass(beanClass);
            rootBeanDefinition.setLazyInit(false); / / must be registered to realize injection parserContext getRegistry () registerBeanDefinition (id, rootBeanDefinition); String name = element.getAttribute("name");
            String age = element.getAttribute("age");
            String language = element.getAttribute("language");
            String isHide = element.getAttribute("isHide");
            MutablePropertyValues pvs = rootBeanDefinition.getPropertyValues();
            pvs.add("name", name);
            pvs.add("age", Integer.valueOf(age));
            pvs.add("language", language);
            pvs.add("hide", isHide.equals(null) ? false : Boolean.valueOf(isHide));

            return rootBeanDefinition;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}
</pre>
Copy the code

This class is implemented from BeanDefinitionParser, and overrides the parse method, which takes two parameters. The first Element can be interpreted as the entity corresponding to the bean configured in Spring XML. The value of the configured parameter is obtained through the element.getAttribute method. The second parameter, ParserContext, can be understood as the interface object provided by Spring to implement the injection of the registered bean. The kv collection of properties of the custom bean is obtained through the getPropertyValues method of the RootBeanDefinition entity object, and property values are added to it. Note: Key in KV set is not the attribute name in entity class, but the parameter name of setter method corresponding to the attribute. For example, if Boolean parameter is named with is, when setter method is automatically generated by editor, is will be removed from the parameter of corresponding setter method. And the string behind the camel name rule processing. Of course, if you want to get around it, you can write your own setter methods.

The registration Handler and XSD Schema Spring specify two XML registration files, and specify that these two files must be in the meta-INF directory of the project resources directory, and the file name and format must be fixed.

Spring. handlers is used to register the Handler class implemented in step 3

As follows:

Code.fengzheng.com/schema/kite…

This is a key-value pair form, and the equals sign is preceded by the namespace that we mentioned in the first step, which is used here, and the equals sign is followed by the full class name of the Handler class. Note the escape character before the colon

Spring.schemas are used to register the XSD files in step 2

As follows:

Code.fengzheng.com/schema/kite…

The equal sign is preceded by the declared XSD path and followed by the actual XSD path.

6. Use in the Spring configuration file

<pre language="typescript" code_block="true"> <? xml version="1.0" encoding="UTF-8"? > <beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:kite="http://code.fengzheng.com/schema/kite"
       xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://code.fengzheng.com/schema/kite http://code.fengzheng.com/schema/kite/kite.xsd">

    <kite:hacker id="hacker" name="moon" language="english" age="18" isHide="true"/>
</pre>
Copy the code

Notice the introduction of namespaces earlier

xmlns:kite=”code.fengzheng.com/schema/kite”

The XSD file location is specified later

http://code.fengzheng.com/schema/kite 
http://code.fengzheng.com/schema/kite/kite.xsd

7, test,

Test by directly obtaining configuration files

<pre language="typescript" code_block="true">public static void main(String[] args){
        ApplicationContext ac = new ClassPathXmlApplicationContext("application.xml");
         Hacker hacker = (Hacker) ac.getBean("hacker");
         System.out.println(hacker.toString());
    }
Copy the code

Test with SpringJUnit4ClassRunner

<pre language="typescript" code_block="true">@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:application.xml" })
public class HackTest {
    @Resource(name = "hacker")
    private Hacker hacker;
    @Test
    public void propertyTest() { System.out.println(hacker.toString()); }}Copy the code

The test results are shown as follows:

Thank you for watching, if you like, you can click the following thank you