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注解