preface
The last article focused on implementing an SPI with interceptor functionality. Today we’ll talk about how the custom SPI integrates with Spring.
Think: What does our implementation of SPI integrate with Spring? Or what features of Spring are we going to use to achieve what we want?
In addition to being well known for IOC and AOP, Spring also provides a wealth of extension points, such as various back-end processors. Today we will talk about the relatively familiar topic of how to inject SPI into the Spring container through custom annotations
Integrated thinking
1. Custom annotations
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Activate {
String value(a) default "";
}
Copy the code
2. Custom beans define scanners
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());
}
Copy the code
3, define 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));
}
Copy the code
4. Customize the Enabled annotation
@Target(value = ElementType.TYPE)
@Retention(value = RetentionPolicy.RUNTIME)
@Documented
@Import(SpiRegister.class)
public @interface EnableSpi {
String[] value() default {};
String[] basePackages() default{}; Class<? >[] basePackageClasses()default {};
}
Copy the code
Example demonstrates
Add the @Activate annotation to the class that you want to inject into the Spring container
@Activate("hello-mysql")
public class SpringMysqlDialect implements SpringSqlDialect {
@Autowired
private MysqlDialectService mysqlDialectService;
@Override
public String dialect(a) {
returnmysqlDialectService.dialect(); }}Copy the code
2. Add the scan SPI range annotation to the startup class
@SpringBootApplication(scanBasePackages = "com.github.lybgeek")
@EnableSpi(basePackages = "com.github.lybgeek")
public class SpiTestApplication implements ApplicationRunner
Copy the code
3. Use getBeansOfType for verification
applicationContext.getBeansOfType(SpringSqlDialect.class)
.forEach((beanName,bean) -> System.out.println(beanName + "-- >" + bean));
Copy the code
The print result is as follows
hello-mysql-->com.github.lybgeek.dialect.mysql.SpringMysqlDialect@433348bc
Copy the code
It has been injected into the Spring container
conclusion
Hosting the project’s services to the Spring IOC container can be regarded as a relatively basic action to integrate with Spring, and this demonstration is also a relatively basic part. The strength of Spring lies in its extensibility. Extension points can be seen everywhere in the life cycle of Spring beans. Interested friends can later experience their own verification
The demo link
Github.com/lyb-geek/sp…