ImportBeanDefinitionRegistrar 动态注册Spring Bean 管理对象

661 阅读2分钟

摘要:

主要介绍了ImportBeanDefinitionRegistrar接口来实现动态注册bean对象,交给spring ioc进行管理,常用两种方式,第一种直接手动注册对象,第二种扫描包的方式按照指定的注解。

ImportBeanDefinitionRegistrar简介

spring官方的@Component@Service等注解的动态注入机制就依赖于它。具体实现一个ImportBeanDefinitionRegistrar的实现类,然后在@Import导入配置类,注意必须要有@Configuration的配置类上,@SpringBootApplication注解已经包含了@Configuration,下文的例子就配置在启动类上。

使用案例

动态注册单一的bean对象

PrintHandler

@Data
public class PrintHandler {

    private static final Logger log = LoggerFactory.getLogger(PrintHandler.class);

    private String desc;

    public void print(String data){
        log.info("[" + this.desc + "] --->" + data);
    }

}

PrintRegistrarConfig

public class PrintRegistrarConfig implements ImportBeanDefinitionRegistrar {

    private static final Logger log = LoggerFactory.getLogger(InterceptorRegistrarConfig.class);

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        log.info("开始动态注册bean");
        BeanDefinition beanDefinition = new GenericBeanDefinition();
        beanDefinition.setBeanClassName(PrintHandler.class.getName());
        MutablePropertyValues values = beanDefinition.getPropertyValues();
        values.addPropertyValue("desc","动态注册");
        registry.registerBeanDefinition("printHandler",beanDefinition);
    }
}
#### 注意事项
> 进行对象注册的时候,对象里面的属性只能通过`beanDefinition.getPropertyValues();`来进行设置属性值,因为这样`spring`才能进行对象的三级缓存管理。

RegistrarApplication类配置

重点是@Import(InterceptorRegistrarConfig.class)这一行,导入配置

@Import(InterceptorRegistrarConfig.class)
@SpringBootApplication
public class RegistrarApplication {

    public static void main(String[] args) {
        SpringApplication.run(RegistrarApplication.class, args);
    }
}

AnyPrintController 控制器编写

@RestController
@RequestMapping(value = "any-print")
public class AnyPrintController {

    @Autowired
    private PrintHandler printHandler;

    @AnyPrint
    @GetMapping("hello")
    public Object hello(String desc){
        printHandler.print(desc);
        return "hello";
    }


}

测试

  • 访问

curl http://127.0.0.01:8080/any-print/hello?desc=zhangsan

  • 结果

仿照@Service注解扫描指定包下的所有有注解@OpenApi的类注册为SpringBean对象

在该注解标注的类中可以使用和spring所有一致的功能

OpenApi注解

@Documented
@Inherited
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface OpenApi {


}

PrintOpenApi

下面我注入了一个AnyPrintService,你可以换成任何你自己定义的对象,由于bean的管理交给了spring,所以可以无差别使用对象注入

@OpenApi
public class PrintOpenApi {

    @Autowired
    private AnyPrintService anyPrintService;

    private static final Logger log = LoggerFactory.getLogger(PrintOpenApi.class);

    public void print(String data){
        log.info("this is print open api : "+ data);
        anyPrintService.hello();
    }

}

OpenApiClassPathBeanDefinitionScanner扫描注解类

public class OpenApiClassPathBeanDefinitionScanner extends ClassPathBeanDefinitionScanner {

    public OpenApiClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters) {
        super(registry, useDefaultFilters);
    }

    protected void registerFilters () {
        /**
         *  TODO addIncludeFilter  满足任意includeFilters会被加载
         */
        addIncludeFilter(new AnnotationTypeFilter(OpenApi.class));
        /**
         *  TODO addExcludeFilter 同样的满足任意excludeFilters不会被加载
         */
        // addExcludeFilter(new AnnotationTypeFilter(MyService.class));
    }

    @Override
    protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
        return super.doScan(basePackages);
    }
}

OpenApiBeanDefinitionRegistrar注册beanspring

public class OpenApiBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, BeanFactoryAware {

    private ResourceLoader resourceLoader;

    private BeanFactory beanFactory;

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        OpenApiClassPathBeanDefinitionScanner scanner = new OpenApiClassPathBeanDefinitionScanner(registry,false);
        scanner.setResourceLoader(this.resourceLoader);
        scanner.registerFilters();
        scanner.doScan("com.github.huzhihui.registrar.openapi");
    }

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

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
    }
}

测试类

@RestController
@RequestMapping(value = "open-api")
public class OpenApiController {

    @Autowired
    private PrintOpenApi printOpenApi;

    @GetMapping(value = "print")
    public Object print(String data){
        printOpenApi.print(data);
        return "SUCCESS";
    }

}
  • 测试

curl http://localhost:8080/open-api/print?data=123