springboot自定义注解,项目启动时扫描注解类并注入容器

以下是核心流程的实现示例,如果需要更完整的实现,可参考:
注意:需要切换到simple-rpc-like-feign分支

一、需求说明

  • 有两个自定义注解@EnableSimpleRpcClients@SimpleRpcClient

    • @EnableSimpleRpcClients注解标注在启动类上,并且可以指定要扫描的包(basePackages)

      @Retention(RetentionPolicy.RUNTIME)
      @Target(ElementType.TYPE)
      @Documented
      public @interface EnableSimpleRpcClients {
      
          String[] value() default {};
      
          String[] basePackages() default {};
      }
      
    • @SimpleRpcClient注解标注在需要注入到容器的类或接口上

      @Documented
      @Target(ElementType.TYPE)
      @Retention(RetentionPolicy.RUNTIME)
      public @interface SimpleRpcClient {
      
          @AliasFor("name")
          String value() default "";
      
          @AliasFor("value")
          String name() default "";
      }
      
  • 要求项目启动时自动扫描@EnableSimpleRpcClients注解的basePackages属性指定的包路径下的所有标注了@SimpleRpcClient注解的类或接口,并将这些类或接口都注入到容器中

二、实现过程分析

主要是借助spring留给我们的扩展点ImportBeanDefinitionRegistrar以及一些工具类BeanDefinitionReaderUtils

  • 利用ImportBeanDefinitionRegistrar我们就可以在容器启动前期,扫描指定basePackages下所有标注了@SimpleRpcClient注解的类或接口信息
  • 然后借助spring提供的工具类BeanDefinitionReaderUtils,将扫描到的类或接口按自定义逻辑注入容器即可。(比如我这里是扫描到basePackages包下,所有标注了@SimpleRpcClient注解的接口,然后借助factoryBean为这些接口创建【动态代理类】,然后将动态代理类注入容器中)

三、代码实现

①、先定义两个自定义注解

EnableSimpleRpcClients

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
public @interface EnableSimpleRpcClients {

    String[] value() default {};

    String[] basePackages() default {};
}

SimpleRpcClient

@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface SimpleRpcClient {

    @AliasFor("name")
    String value() default "";

    @AliasFor("value")
    String name() default "";
}

②、自定义ImportBeanDefinitionRegistrar

@Component
public class ExampleRegistrar implements ImportBeanDefinitionRegistrar,
ResourceLoaderAware, EnvironmentAware {
  /**
     * 资源加载器
     */
    private ResourceLoader resourceLoader;
    /**
     * 环境
     */
    private Environment environment;

    @Override
    public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
        // 创建scanner
        ClassPathScanningCandidateComponentProvider scanner = getScanner();
        scanner.setResourceLoader(resourceLoader);

        // 设置扫描器scanner扫描的过滤条件
        AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(SimpleRpcClient.class);
        scanner.addIncludeFilter(annotationTypeFilter);

        // 获取指定要扫描的basePackages
        Set<String> basePackages = getBasePackages(metadata);

        // 遍历每一个basePackages
        for (String basePackage : basePackages) {
            // 通过scanner获取basePackage下的候选类(有标@SimpleRpcClient注解的类)
            Set<BeanDefinition> candidateComponents = scanner.findCandidateComponents(basePackage);
            // 遍历每一个候选类,如果符合条件就把他们注册到容器
            for (BeanDefinition candidateComponent : candidateComponents) {
                if (candidateComponent instanceof AnnotatedBeanDefinition) {
                    // verify annotated class is an interface
                    AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent;
                    AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();
                    Assert.isTrue(annotationMetadata.isInterface(), "@SimpleRpcClient can only be specified on an interface");
                    // 获取@SimpleRpcClient注解的属性
                    Map<String, Object> attributes = annotationMetadata.getAnnotationAttributes(SimpleRpcClient.class.getCanonicalName());
                    // 注册到容器
                    registerSimpleRpcClient(registry, annotationMetadata, attributes);
                }
            }
        }
    }
  
  /**
     * 利用factoryBean创建代理对象,并注册到容器
     */
    private static void registerSimpleRpcClient(BeanDefinitionRegistry registry,
                                                AnnotationMetadata annotationMetadata,
                                                Map<String, Object> attributes) {
        // 类名(接口全限定名)
        String className = annotationMetadata.getClassName();
        // 创建SimpleRpcClientFactoryBean的BeanDefinition
        BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(SimpleRpcClientFactoryBean.class);
        // 解析出@SimpleRpcClient注解的name
        String name = getName(attributes);
        if (!StringUtils.hasText(name)) {
            throw new ProviderNameNullException(String.format("class [%s] , @SimpleRpcClient name or value can not be null, please check.", className));
        }

        // 给factoryBean添加属性值
        definition.addPropertyValue("name", name);
        definition.addPropertyValue("type", className);
        definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
        String alias = name + "SimpleRpcClient";
        AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();

        // 注册bean定义信息到容器
        BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className, new String[]{alias});
      	// 使用BeanDefinitionReaderUtils工具类将BeanDefinition注册到容器
        BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
    }

    /**
     * 创建扫描器
     */
    protected ClassPathScanningCandidateComponentProvider getScanner() {
        return new ClassPathScanningCandidateComponentProvider(false, environment) {
            @Override
            protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
                boolean isCandidate = false;
                if (beanDefinition.getMetadata().isIndependent()) {
                    if (!beanDefinition.getMetadata().isAnnotation()) {
                        isCandidate = true;
                    }
                }
                return isCandidate;
            }
        };
    }

    /**
     * 获取base packages
     */
    protected static Set<String> getBasePackages(AnnotationMetadata importingClassMetadata) {
        // 获取到@EnableSimpleRpcClients注解所有属性
        Map<String, Object> attributes = importingClassMetadata.getAnnotationAttributes(EnableSimpleRpcClients.class.getCanonicalName());
        Set<String> basePackages = new HashSet<>();
        assert attributes != null;
        // value 属性是否有配置值,如果有则添加
        for (String pkg : (String[]) attributes.get("value")) {
            if (StringUtils.hasText(pkg)) {
                basePackages.add(pkg);
            }
        }

        // basePackages 属性是否有配置值,如果有则添加
        for (String pkg : (String[]) attributes.get("basePackages")) {
            if (StringUtils.hasText(pkg)) {
                basePackages.add(pkg);
            }
        }

        // 如果上面两步都没有获取到basePackages,那么这里就默认使用当前项目启动类所在的包为basePackages
        if (basePackages.isEmpty()) {
            basePackages.add(ClassUtils.getPackageName(importingClassMetadata.getClassName()));
        }

        return basePackages;
    }

    /**
     * 获取name
     */
    protected static String getName(Map<String, Object> attributes) {
        String name = (String) attributes.get("name");
        if (!StringUtils.hasText(name)) {
            name = (String) attributes.get("value");
        }
        return name;
    }
  
  @Override
    public void setEnvironment(Environment environment) {
        this.environment = environment;
    }

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

③、具体使用

// 启动类上标注@EnableSimpleRpcClients注解,如果不指定value和basePackages那么默认扫描classpath下
@EnableSimpleRpcClients
@SpringBootApplication
public class SimpleRpcConsumerApplication {

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

}
上一篇:GoldenGate自动归档discard文件


下一篇:CodingSouls团队项目冲刺(4)-个人概况