Чтобы понять Spring IoC, вы должны сначала узнать, как расширить пользовательские bean-компоненты Spring.

Java

Я Кайт, официальный аккаунт "Воздушный змей в древности", технический официальный аккаунт не только с технологиями, но и слэш-разработчик 6 лет, много лет в кружке программирования, в основном работающий на Java, а также на Python и реагировать. Серия статей Spring Cloud завершена, и вы можете перейти кмой гитхабПолную серию смотрите на . Вы также можете ответить на «pdf» в официальном аккаунте, чтобы получить мою тщательно созданную версию полного руководства в формате pdf.

Сегодняшняя статья является разминкой для следующей статьи о реализации Spring IoC.Основной частью Spring является инверсия управления, а объектами управления являются различные bean-компоненты.

Хотя большинство команд сейчас используют Spring Boot напрямую, а Spring MVC используется немногими, основой по-прежнему является Spring, но в большей степени конфигурация XML изменена на форму аннотаций.

Если вы когда-либо использовали форму конфигурации XML, то вы знаете<context:component-scan>,<bean>,<aop:aspectj-autoproxy>Как реализованы эти конфигурации тегов? Зная их, я считаю, что это очень поможет вам лучше понять Spring.

Давай, приступим!

Spring mvc предоставляет механизм для расширения xml для написания пользовательских компонентов xml, таких как инфраструктура dubbo, которая использует этот механизм для реализации множества компонентов dubbo, таких как<dubbo:application>,<dubbo:registry>Подождите, просто установите это стандартное расширение для реализации конфигурации.

Какой смысл расширять пользовательский bean-компонент

Предположим, мы хотим использовать фреймворк с открытым исходным кодом или набор API, нам определенно нужны следующие две вещи:

  1. Простота использования, то есть настройка проста, чем меньше мест для настройки, тем лучше

  2. Инкапсуляция, простая в вызове, то есть чем выше инкапсуляция, тем лучше, и нижележащая реализация менее подвержена риску.

Основываясь на двух вышеприведенных пунктах, предполагая, что мы хотим реализовать пользовательскую функцию, ее также можно реализовать с помощью существующих элементов конфигурации Spring, но может быть больше содержимого для настройки, а также может быть добавлена ​​поддержка кода. Это приводит к разрозненной логике и неудобному обслуживанию.

Поэтому мы инкапсулируем некоторые пользовательские сложные функции, расширяя конфигурацию Spring, чтобы минимизировать конфигурацию.

Действия по внедрению пользовательского расширения

Этот пример является лишь простой демонстрацией, и функция проста, то есть реализовать компонент Hacker с настраиваемыми параметрами, а затем предоставить метод toString() для ввода информации о параметрах. Наша окончательная конфигурация bean-компонента выглядит следующим образом:

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

Сравните с собственной конфигурацией Spring, например:

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

1. Реализуйте собственный класс компонента с именем Hacker и перегрузите метод toString() в методе, введите имя свойства, и код выглядит следующим образом:

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");
        return builder.toString();
    }
}

2. Напишите файл описания атрибута схемы xsd с именем hacker.xsd и поместите его в каталог META-INF в каталоге ресурсов проекта (местоположение можете определить сами), что можно понять так: этот файл соответствует только что созданный класс сущности Создайте описание структуры xml, содержание которого выглядит следующим образом:

<?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>

Обратите внимание на приведенное выше

xmlns="http://code.fengzheng.com/schema/kite

и

targetNamespace="http://code.fengzheng.com/schema/kite"

Через какое-то время будет где его использовать.

3. Реализовать класс NamespaceHandler, код такой:

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("执行 HackNamespaceHandler 的 init 方法");
        registerBeanDefinitionParser("hacker",new HackBeanDefinitionParser(Hacker.class));
        logger.info("注册 「hacker」 定义转换器成功");
    }
}

Этот вид функции очень прост, то есть наследуется класс NamespaceHandlerSupport, перегружается метод init и вызывается метод registerBeanDefinitionParser, где первый параметр hacker — это имя, которое мы будем использовать в файле конфигурации spring позже, то естьkite:hackerХакер здесь; Второй параметр — что говорить дальше.

4. Реализовать класс BeanDefinitionParser. Функция этого класса состоит в том, чтобы просто связать класс, реализованный на первом этапе, с bean-компонентом, объявленным в Spring xml, и реализовать внедрение атрибутов. Давайте посмотрим на код:

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("进入 HckBeanDefinitionParser 的 parse 方法");
        try {
            String id = element.getAttribute("id");
            RootBeanDefinition rootBeanDefinition = new RootBeanDefinition();
            rootBeanDefinition.setBeanClass(beanClass);
            rootBeanDefinition.setLazyInit(false);

            //必须注册才可以实现注入
            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;
    }
}

Этот класс реализован из BeanDefinitionParser и перегружает метод parse.Метод parse имеет два параметра.Первый Element можно понимать как сущность, соответствующую бину, сконфигурированному Spring xml, а настроенное значение параметра можно получить через element.getAttribute Второй параметр ParserContext можно понимать как интерфейсный объект, предоставляемый Spring, через который реализуется внедрение зарегистрированных bean-компонентов. С помощью метода getPropertyValues ​​объекта сущности RootBeanDefinition можно получить коллекцию свойств kv пользовательского компонента, а затем добавить к ней значение свойства. Примечание: ключом в коллекции kv является не имя свойства в классе сущностей, а имя параметра метода установки, соответствующего свойству.Например, если имя логического параметра начинается с is, когда редактор используется для автоматически сгенерировать метод установки, соответствующий параметр настройки метода будет удален, и следующая строка будет обработана правилом именования регистра верблюда. Конечно, если вы хотите избежать этого, вы можете написать свой собственный метод установки.

5. Зарегистрируйте обработчик и схему xsd Spring предусматривает два регистрационных файла xml и предусматривает, что эти два файла должны находиться в каталоге META-INF в каталоге ресурсов проекта, а имя и формат файла должны быть фиксированными.

spring.handlers используется для регистрации класса Handler, реализованного на третьем шаге.

Содержание следующее:

http\://code.fengzheng.com/schema/kite=kite.lab.spring.config.HackNamespaceHandler

Это в форме пары ключ-значение.Пространство имен находится перед знаком равенства.Оно было упомянуто на первом шаге и используется здесь.После знака равенства находится полное имя класса класса Handler. Обратите внимание на escape-символ перед двоеточием

spring.schemas используется для регистрации файла xsd на втором этапе.

Содержание следующее:

http\://code.fengzheng.com/schema/kite/kite.xsd=META-INF/hacker.xsd

Знаку равенства предшествует объявленный путь xsd, за которым следует фактический путь xsd.

6. Использовать в конфигурационном файле Spring

<?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"/>
</beans>

Обратите внимание, что пространства имен были введены ранее

xmlns:kite="http://code.fengzheng.com/schema/kite"

Расположение файла xsd указывается позже.

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

7. Тест

Протестируйте способ получения файла конфигурации напрямую

public static void main(String[] args){
        ApplicationContext ac = new ClassPathXmlApplicationContext("application.xml");
         Hacker hacker = (Hacker) ac.getBean("hacker");
         System.out.println(hacker.toString());
    }

Тестирование с помощью SpringJUnit4ClassRunner

@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());
    }
}

Результаты испытаний представлены на рисунке:

Эта статья лишь кратко описывает этапы реализации. Для конкретных операций обратитесь к dubbo. Код находится в модуле dubbo-config-spring. Конечно, вы также можете прочитать исходный код Spring, например, просмотретьcontext:component-scanреализация в модуле spring-context-version-number.

Творенье не простое, маленькая похвала, большое тепло, приди и согрей меня. Пожалуйста, похвалите меня!

Я Kite, официальный аккаунт "Kite in Ancient Times", слэш-разработчик, много лет в кружке программирования, в основном работаю на Java, а также очень хорошо играю на Python и React. Вы можете добавить меня в друзья в официальном аккаунте, вступить в группу для общения и обучения, так же в группе много одноклассников с крупных заводов.

Категории