SpringBoot中Import注解与ImportSelector、ImportBeanDefinitionRegistrar接口

一、Import注解可以装配Bean

  1、单独使用Import注解,普通类作为参数

public class User {}

public class Role {}

public class MyConfig {

    @Bean
    public Runnable createRunnable(){
        return () -> {};
    }
}
@SpringBootApplication
@Import({User.class,Role.class,MyConfig.class})
public class App {

    public static void main(String[] args) {
        ConfigurableApplicationContext context=SpringApplication.run(App.class,args);
        System.out.println(context.getBean(User.class));
        System.out.println(context.getBean(Role.class));
        System.out.println(context.getBeansOfType(Runnable.class));
        context.close();
    }
}

  2、ImportSelector接口的实现类作为Import的参数

public class MyImportSelector implements ImportSelector {

    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        String[] names = {"com.leiyuke.configuration.config.User",Role.class.getName(),MyConfig.class.getName()};
        return names;
    }
}
@SpringBootApplication
@Import(MyImportSelector.class)
public class App {

    public static void main(String[] args) {
        ConfigurableApplicationContext context=SpringApplication.run(App.class,args);
        System.out.println(context.getBean(User.class));
        System.out.println(context.getBean(Role.class));
        System.out.println(context.getBeansOfType(Runnable.class));
        context.close();
    }
}

  3、ImportBeanDefinitionRegistrar接口的实现类作为Import的参数

public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {

    @Override
    public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
        {
            BeanDefinitionBuilder bdb = BeanDefinitionBuilder.rootBeanDefinition(User.class);
            beanDefinitionRegistry.registerBeanDefinition("user", bdb.getBeanDefinition());

            BeanDefinitionBuilder bdb2 = BeanDefinitionBuilder.rootBeanDefinition(Role.class);
            beanDefinitionRegistry.registerBeanDefinition("role", bdb2.getBeanDefinition());

            BeanDefinitionBuilder bdb3 = BeanDefinitionBuilder.rootBeanDefinition(MyConfig.class);
            beanDefinitionRegistry.registerBeanDefinition(MyConfig.class.getName(), bdb3.getBeanDefinition());
        }
    }
}
@SpringBootApplication
@Import(MyImportBeanDefinitionRegistrar.class)
public class App {

    public static void main(String[] args) {
        ConfigurableApplicationContext context=SpringApplication.run(App.class,args);
        System.out.println(context.getBean(User.class));
        System.out.println(context.getBean(Role.class));
        System.out.println(context.getBeansOfType(Runnable.class));
        context.close();
    }
}

二、Import注解用于自定义注解上,可获取其属性

  1、使用ImportSelector获取属性

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(MyImportSelector.class)
public @interface EnableReadBook {
    String bookname();
    double bookPrice();
}
public class MyImportSelector implements ImportSelector {

    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        System.out.println(annotationMetadata.getAnnotationAttributes(EnableReadBook.class.getName()));
        String[] names = {"com.leiyuke.configuration.config.User",Role.class.getName(),MyConfig.class.getName()};
        return names;
    }
}
@SpringBootApplication
@EnableReadBook(bookname="测试",bookPrice=12.34)
public class App {

    public static void main(String[] args) {
        ConfigurableApplicationContext context=SpringApplication.run(App.class,args);
        System.out.println(context.getBean(User.class));
        System.out.println(context.getBean(Role.class));
        System.out.println(context.getBeansOfType(Runnable.class));
        context.close();
    }
}

  会输出:{bookPrice=12.34, bookname=测试}

  2、使用ImportBeanDefinitionRegistrar获取属性

   1)类比ImportSelector,先看一简单情况

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(MyImportBeanDefinitionRegistrar.class)
public @interface EnableReadBook {
    String bookname();
    double bookPrice();
}
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {

    @Override
    public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {

        Map<String,Object> attr=(Map<String,Object>)annotationMetadata.getAnnotationAttributes(EnableReadBook.class.getName());
        System.out.println(attr);

        BeanDefinitionBuilder bdb = BeanDefinitionBuilder.rootBeanDefinition(User.class);
        beanDefinitionRegistry.registerBeanDefinition("user", bdb.getBeanDefinition());

        BeanDefinitionBuilder bdb2 = BeanDefinitionBuilder.rootBeanDefinition(Role.class);
        beanDefinitionRegistry.registerBeanDefinition("role", bdb2.getBeanDefinition());

        BeanDefinitionBuilder bdb3 = BeanDefinitionBuilder.rootBeanDefinition(MyConfig.class);
        beanDefinitionRegistry.registerBeanDefinition(MyConfig.class.getName(), bdb3.getBeanDefinition());
    }
}
@SpringBootApplication
@EnableReadBook(bookname="测试",bookPrice=12.34)
public class App {

    public static void main(String[] args) {
        ConfigurableApplicationContext context=SpringApplication.run(App.class,args);
        System.out.println(context.getBean(User.class));
        System.out.println(context.getBean(Role.class));
        System.out.println(context.getBeansOfType(Runnable.class));
        context.close();
    }
}

  会输出:{bookname=测试, bookPrice=12.34}

  2)使用ImportBeanDefinitionRegistrar给需要装配的Bean添加属性

  如下:装配EchoBeanPostProcessor为bean,如何给属性packages赋值?

public class EchoBeanPostProcessor implements BeanPostProcessor {

    private List<String> packages;

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        for (String pack : packages){
            if(bean.getClass().getName().startsWith(pack)){
                System.out.println("echo bean:"+bean.getClass().getName()+" "+pack);
            }
        }
        return bean;
    }

   ... ... //get set 方法
}
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {

    @Override
    public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {

        Map<String,Object> attr=(Map<String,Object>)annotationMetadata.getAnnotationAttributes(EnableEcho.class.getName());
        String[] packArr = (String[])attr.get("packages");
        List<String> packages = Arrays.asList(packArr);

        System.out.println("packages:"+packages);

        BeanDefinitionBuilder bdb = BeanDefinitionBuilder.rootBeanDefinition(EchoBeanPostProcessor.class);
        bdb.addPropertyValue("packages",packages);
        beanDefinitionRegistry.registerBeanDefinition(EchoBeanPostProcessor.class.getName(),bdb.getBeanDefinition());
    }
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(MyImportBeanDefinitionRegistrar.class)
public @interface EnableEcho {
    String[] packages();
}
@SpringBootApplication
@EnableEcho(packages = {"com.leiyuke.configuration.bean1","com.leiyuke.configuration.bean2"})
public class App {

    public static void main(String[] args) {
        ConfigurableApplicationContext context=SpringApplication.run(App.class,args);
        context.close();
    }
}
package com.leiyuke.configuration.bean1;

@Component
public class Cat {
}

package com.leiyuke.configuration.bean2;

@Component
public class Dog {
}

说明:BeanPostProcessor是bean的后置处理器,每装配一个bean都会调用该接口的方法。

   程序启动,先装配EchoBeanPostProcessor,该类的属性packages也完成传参。

   装配Cat、Dog的时候,会调用EchoBeanPostProcessor类的postProcessBeforeInitialization方法。

   此时候会输出:

   echo bean:com.leiyuke.configuration.bean1.Cat com.leiyuke.configuration.bean1
   echo bean:com.leiyuke.configuration.bean2.Dog com.leiyuke.configuration.bean2

三、总结

  1、SpringBoot中可以使用Import注解配合ImportSelector、ImportBeanDefinitionRegistrar接口实现自定义注解的传参

  2、这个自定义注解一般以Enable开头,以启动某个特性

  3、SpringBoot能自动解析配置文件中的属性是因为其默认实现了EnableConfigurationProperties

  4、EnableConfigurationProperties的Import中指定了需要ConfigurationProperties注解

  5、我们开发中没有使用过EnableConfigurationProperties是因为EnableAutoConfiguration注解包含了它

  6、SpringBootApplication注解中包了EnableAutoConfiguration注解

 

posted @ 2019-11-17 23:28  雷雨客  阅读(1013)  评论(0编辑  收藏  举报