上一篇文章主要[聊聊如何实现一个带有拦截器功能的SPI]。今天就来聊聊自定义的SPI如何与spring整合。
思考:我们实现的SPI要整合spring哪些东西?或者我们要利用spring的哪些特性实现我们哪些东西?
spring除了被大家熟知的IOC和AOP之外,还有它也提供了很丰富的扩展点,比如各种后置处理器,今天我们就聊聊大家相对熟悉的话题,如何通过自定义注解把SPI注入到spring容器中
@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface Activate { String value() default ""; }
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()); }
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)); }
@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的生命周期中,基本上随处可见扩展点,感兴趣的朋友后续可以自行体会验证
https://github.com/lyb-geek/springboot-learning/tree/master/springboot-spi-enhance/springboot-spi-framework-spring