关于java:聊聊自定义实现的SPI如何与spring进行整合

58次阅读

共计 2891 个字符,预计需要花费 8 分钟才能阅读完成。

前言

上一篇文章次要聊聊如何实现一个带有拦截器性能的 SPI。明天就来聊聊自定义的 SPI 如何与 spring 整合。

思考:咱们实现的 SPI 要整合 spring 哪些货色? 或者咱们要利用 spring 的哪些个性实现咱们哪些货色?

spring 除了被大家熟知的 IOC 和 AOP 之外,还有它也提供了很丰盛的扩大点,比方各种后置处理器,明天咱们就聊聊大家绝对相熟的话题,如何通过自定义注解把 SPI 注入到 spring 容器中

整合思路

1、自定义注解

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Activate {String value() default "";
}

2、自定义 bean 定义扫描器

public class ActivateClassPathBeanDefinitionScanner extends ClassPathBeanDefinitionScanner {public ActivateClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry) {super(registry);
    }


    @SneakyThrows
    @Override
    protected void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) {super.registerBeanDefinition(definitionHolder, registry);
        Class clz = Class.forName(definitionHolder.getBeanDefinition().getBeanClassName());
        Activate activate = AnnotationUtils.findAnnotation(clz,Activate.class);
        if(ObjectUtils.isNotEmpty(activate) && StringUtils.isNotBlank(activate.value())){String activateName = getEnvironment().resolvePlaceholders(activate.value());
            registry.registerBeanDefinition(activateName,definitionHolder.getBeanDefinition());
        }
    }

    @Override
    protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {return super.isCandidateComponent(beanDefinition) && beanDefinition.getMetadata()
                .hasAnnotation(Activate.class.getName());
    }

3、定义 ImportBeanDefinitionRegistrar

public class SpiRegister implements ImportBeanDefinitionRegistrar, EnvironmentAware {

    private Environment environment;


    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {Set<String> basePackages = this.getBasePackages(importingClassMetadata);
        String[] packages = {};
        SpiBeanUtils.registerActivateInstances(registry,environment,basePackages.toArray(packages));
    }

4、自定义 enabled 注解

@Target(value = ElementType.TYPE)
@Retention(value = RetentionPolicy.RUNTIME)
@Documented
@Import(SpiRegister.class)
public @interface EnableSpi {String[] value() default {};

 
    String[] basePackages() default {};

  
    Class<?>[] basePackageClasses() default {};}

示例演示

1、在须要注入到 spring 容器的类上加上 @Activate 注解

@Activate("hello-mysql")
public class SpringMysqlDialect implements SpringSqlDialect {

    @Autowired
    private MysqlDialectService mysqlDialectService;

    @Override
    public String dialect() {return mysqlDialectService.dialect();
    }


}

2、启动类上加上扫描 SPI 范畴注解

@SpringBootApplication(scanBasePackages = "com.github.lybgeek")
@EnableSpi(basePackages = "com.github.lybgeek")
public class SpiTestApplication implements ApplicationRunner 

3、利用 getBeansOfType 进行验证

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

打印后果如下

hello-mysql-->com.github.lybgeek.dialect.mysql.SpringMysqlDialect@433348bc

阐明曾经注入到 spring 容器中

总结

把我的项目的服务托管给 spring ioc 容器,能够算是与 spring 整合比拟根底的动作,本文演示也是绝对根底的一环,spring 弱小的中央,在于它的扩展性,在 spring bean 的生命周期中,基本上随处可见扩大点,感兴趣的敌人后续能够自行领会验证

demo 链接

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

正文完
 0