Как реализовать функцию динамического внедрения Spring, аналогичную @Component

Spring Boot Java

1. Введение

мы вПредыдущийправильноMybatisкакMapperинтерфейсная инъекцияSpring IoCПосле анализа некоторые студенты спросили Толстого Брата, какая польза от этого, этот эффект на самом деле довольно большой, например, он позволяет добиться аналогичного@Controllerаннотаций (или наследовать единый интерфейс) для выполнения, таких как унифицированная инъекция запланированных задач илиWebsocketУнифицированная инъекция процессоров и т. д. будет иметь некоторые общиеBeanДинамический впрыск.

// 模仿 Controller  
@XBean(description = "ETL JOB")
public class JobShedule {

    @Caller(cron = "* * 0/5 * * ?")
    public void exec(){
        // job 
    }
}

Вышеупомянутый псевдокод представляет собой синхронизированный компонент задачи, который имитирует контроллер.

2. Идеи дизайна

Я обобщил подробные идеи разработки и дизайна, и вы можете реализовать эту функцию, если будете следовать инструкциям.

2.1 Определение аннотаций сканирования

определить что-то вроде@MappScanимпортировать настройкиImportBeanDefinitionRegistrarи укажите диапазон сканируемых пакетов.

@Documented
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Import(XBeanDefinitionRegistrar.class)
public @interface XBeanScan {

    String[] basePackages();
}

Мы настроили аннотацию скана@XBeanScan. Он имеет две функции:

  • пройти черезbasePackagesЗадает область сканирования пакетов.
  • импортировать наш обычайImportBeanDefinitionRegistrarреализацияXBeanDefinitionRegistrar.

2.2 Определение общих тегов для целевых компонентов

Обычно мы можем выбрать интерфейс идентификации, и все его реализующие классы будут внедрены.Spring IoC; или с более удобной аннотацией все классы, помеченные этой аннотацией, будут инжектированыSpring IoC. Здесь мы используем более гибкие и удобные аннотации для реализации@XBeanАннотация маркера:

@Documented
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface XBean {
    String description() default "";
}

2.3 Внедрение сканера

SpringФреймворк предоставляет нам сканеры для регистрации помеченныхBean, этопоследний разделУпомянулClassPathBeanDefinitionScanner, мы наследуем его с небольшой модификацией:

public class XBeanDefinitionScanner extends ClassPathBeanDefinitionScanner {
    public XBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters) {
        super(registry, useDefaultFilters);
        super.addIncludeFilter(new AnnotationTypeFilter(XBean.class));
    }
}

Здесь мы не используем фильтр по умолчанию, мы указываем, что цель, сканируемая сканером,@XBeanотмеченныеBean.

2.4 Реализация реестра компонентов

Главное событие здесь, нам нужно2.1прибыть2.3Эти компоненты определены вImportBeanDefinitionRegistrarсобирается в реализации.

/**
 * The type X bean definition registrar.
 *
 * @author felord.cn
 * @since 2020 /9/18 22:59
 */
public class XBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {
    private ResourceLoader resourceLoader;

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        // 不使用默认过滤器
        XBeanDefinitionScanner xBeanDefinitionScanner = new XBeanDefinitionScanner(registry, false);
        xBeanDefinitionScanner.setResourceLoader(resourceLoader);
        // 扫描XBeanScan注解指定的包
        xBeanDefinitionScanner.scan(getBasePackagesToScan(importingClassMetadata));
    }

    @Override
    public void setResourceLoader(ResourceLoader resourceLoader) {
        this.resourceLoader = resourceLoader;
    }

    /**
     * 获取{@link XBeanScan}中声明的扫描包路径
     * @param metadata the meta
     * @return  包路径数组
     */
    private String[] getBasePackagesToScan(AnnotationMetadata metadata) {
        String name = XBeanScan.class.getName();
        AnnotationAttributes attributes = AnnotationAttributes.fromMap(metadata.getAnnotationAttributes(name, true));
        Assert.notNull(attributes, () -> "No auto-configuration attributes found. Is " + metadata.getClassName()
                + " annotated with " + ClassUtils.getShortName(name) + "?");
        return attributes.getStringArray("basePackages");
    }
}

из метаданных аннотацийimportingClassMetadataРазбираем нужный нам путь сканированияbasePackagesДождитесь метаданных, а затем позвольте сканеру просканировать этот путь.

2.5 Использование

С участием@ConfigurationКласс помечен илиSpring BootизMainиспользовать на уроке@XBeanScanВот и все, не так ли просто!

фактически@ComponentScanобеспечивает аналогичный функционал.

3. Резюме

Эта статья правильнаяПредыдущийКонкретное применение теории, честно говоря, последняя статья была скучной или даже не в состоянии уловить главное, но иногда теория такая. Как только вы прочитаете ее вместе с этой статьей, вы вдруг поймете. Если вам нужен более детальный контроль, добавьте этиBeanDefinitionRegistryPostProcessorа такжеFactoryBeanЖдатьSpringпредоставленный функциональный интерфейс. Что вам нужно узнать из этих двух статей, так это то, как использовать существующие компоненты для реализации собственной логики, читая исходный код по аналогии. Это большое улучшение для вас. Ну вот и все на сегодня, уделяйте больше внимания:Код Фермер Маленький Толстый БратВас ждут еще галантереи.

关注公众号:Felordcn获取更多资讯

Личный блог: https://felord.cn