簡要
有的時候需要動態注入bean到spring容器中,@service,@component 滿足不了,還可以在class上的根據注解來進行擴展,例如我想根據注解里的多個id來進行注入spring容器中,不用創建每個id來寫@component,然后根據id中獲取實例,還可以動態注入一些需要的屬性,等等。
解決方案還是有的,而且還不止一種,這都得虧於spring的設計擴展性太強,根據不同時刻滿足不同需求,我這邊分別用2中方式BeanDefinitionRegistryPostProcessor和ImportBeanDefinitionRegistrar進行切入口
1.BeanDefinitionRegistryPostProcessor
結合@Import使用
spring官方就是用這種方式,實現@Component、@Service等注解的動態注入機制。定義一個ImportBeanDefinitionRegistrar的實現類,然后在有@Configuration注解的配置類上使用@Import導入。
類似mybatis @Mapper和@MapperScan結合使用,可以指定注解參數,並進行處理業務,比如需要掃描指定的package進行注入,以及實現開啟注解功能等,擴展性強
2.BeanDefinitionRegistryPostProcessor
這個接口擴展自BeanFactoryPostProcessor,專門用於動態注冊Bean。
其實在spring的生命周期中,bean在實例化之前都是無差別的被當做資源加載進來的,並被封裝成一個個Beandefinition。在spring啟動時,所有bean實例化的注解和xml文件都會被加載進來,並注冊成Beandefinition。准備后續的bean實例化。那么在注冊成 Beandefinition這步,spring其實提供給了我們不少后門進行操作。常見的后置處理器BeanDefinitionRegistryPostProcessor就是一個比較常見的自定義操作bean的接口。BeanDefinitionRegistryPostProcessor是BeanFactoryPostProcessor的子接口, BeanFactoryPostProcessor的作用是在bean的定義信息已經加載但還沒有初始化的時候執行方法postProcessBeanFactory()方法,而BeanDefinitionRegistryPostProcessor是在BeanFactoryPostProcessor的前面執。
實現
- 編寫自定義注解
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface CoreAnnotation { String[] value() default {}; }
注解的value是輸入數組,比如輸入多個id。
- 注解應用
public interface FinService { String say(String arg); }
@CoreAnnotation({"write"}) public class WriteService implements FinService { @Override public String say(String arg) { return "write"; } }
以上就隨意寫了個WriteService對象,用coreAnnotation進行注解並寫了write值傳入。接下來就應該要進行注入bean到spring容器中了
-
BeanDefinitionRegistryPostProcessor
先用實現這個最簡單,只需要實現BeanDefinitionRegistryPostProcessor接口就行,會在啟動后執行一次
先寫了一個公共的注冊方法,都可以用
/** * 注冊 BeanDefinition */ private void registerCandidateComponents(BeanDefinitionRegistry registry, Set<BeanDefinition> candidateComponents) throws ClassNotFoundException { for (BeanDefinition candidateComponent : candidateComponents) { if (candidateComponent instanceof AnnotatedBeanDefinition) { AnnotatedBeanDefinition annotatedBeanDefinition = (AnnotatedBeanDefinition) candidateComponent; AnnotationMetadata annotationMetadata = annotatedBeanDefinition.getMetadata(); Map<String, Object> customImportAnnotationAttributesMap = annotationMetadata.getAnnotationAttributes(CoreAnnotation.class.getName()); AnnotationAttributes customImportAnnotationAttributes = Optional.ofNullable(AnnotationAttributes.fromMap(customImportAnnotationAttributesMap)).orElseGet(AnnotationAttributes::new);
//獲取注解里的值 String[] values = customImportAnnotationAttributes.getStringArray("value"); String className = annotationMetadata.getClassName(); Class<?> clazzName = Class.forName(className); // AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(CustomImportFactoryBean.class) // .addPropertyValue("type", clazzName) // .addPropertyValue("beanName", beanName) // .setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE) // .setRole(BeanDefinition.ROLE_INFRASTRUCTURE) // .getBeanDefinition(); // registry.registerBeanDefinition(beanName, beanDefinition); Arrays.asList(values).forEach(m ->{ RootBeanDefinition mbean = null; try { mbean = new RootBeanDefinition(clazzName); } catch (Exception e) { e.printStackTrace(); } registry.registerBeanDefinition(m, mbean); }); } } }
@Component public class DefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor, ResourceLoaderAware, EnvironmentAware { private Environment environment; private ResourceLoader resourceLoader; @Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException { //用掃描器根據指定注解進行掃描獲取BeanDefinition ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(beanDefinitionRegistry, false, environment, resourceLoader); scanner.addIncludeFilter(new AnnotationTypeFilter(CoreAnnotation.class)); Set<BeanDefinition> candidateComponents = scanner.findCandidateComponents("com"); registerCandidateComponents(beanDefinitionRegistry,candidateComponents); } @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException { } @Override public void setEnvironment(Environment environment) { this.environment = environment; } @Override public void setResourceLoader(ResourceLoader resourceLoader) { this.resourceLoader = resourceLoader; } }
ResourceLoaderAware, EnvironmentAware 這個內置接口,只要實現接口Spring會自動幫你注入,是不是很方便,根據內容來進行對ClassPathBeanDefinitionScanner的掃描獲取指定注解,其中findCandidateComponents方法就是指定掃描包開始位置,
這里寫死了com包下,具體的時候需要根據動態的去獲取,也可以根據指定用戶進行掃描,比如@MapperScan功能
如果不用ClassPathBeanDefinitionScanner,用反射表Reflections來進行查找注解的哪些對象也可以實現,如下
Reflections reflections = new Reflections("com"); Set<Class<? extends FinService>> subTypes = reflections.getSubTypesOf(FinService.class); Set<Class<?>> annotated = reflections.getTypesAnnotatedWith(CoreAnnotation.class); annotated.forEach(x -> { x.getSimpleName(); CoreAnnotation coreAnnotation = x.getAnnotation(CoreAnnotation.class); String[] values = coreAnnotation.value(); if (null != values) { List<String> strings = Arrays.asList(values); strings.forEach(m -> { RootBeanDefinition mbean = null; try { mbean = new RootBeanDefinition(x.newInstance().getClass()); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } beanDefinitionRegistry.registerBeanDefinition(m, mbean); }); } });
-
ImportBeanDefinitionRegistrar
這個要結合@Import來使用
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Import(MyImportBeanDefinitionRegistrar.class) public @interface EnableCustomImport { String[] packages() default {}; }
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware { private Environment environment; private ResourceLoader resourceLoader; @SneakyThrows @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { boolean enableCustomImport = importingClassMetadata.hasAnnotation(EnableCustomImport.class.getName()); //@Import不是在這個EnableCustomImport注解上的不執行 if (!enableCustomImport) { return; } Map<String, Object> annotationAttributesMap = importingClassMetadata.getAnnotationAttributes(EnableCustomImport.class.getName()); AnnotationAttributes annotationAttributes = Optional.ofNullable(AnnotationAttributes.fromMap(annotationAttributesMap)).orElseGet(AnnotationAttributes::new); // 獲取需要掃描的包 String[] packages = retrievePackagesName(importingClassMetadata, annotationAttributes); // useDefaultFilters = false,即第二個參數 表示不掃描 @Component、@ManagedBean、@Named 注解標注的類 ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(registry, false, environment, resourceLoader); scanner.addIncludeFilter(new AnnotationTypeFilter(CoreAnnotation.class)); // 掃描包 for (String needScanPackage : packages) { Set<BeanDefinition> candidateComponents = scanner.findCandidateComponents(needScanPackage); try { registerCandidateComponents(registry, candidateComponents); } catch (ClassNotFoundException e) { e.printStackTrace(); } } } /** * 獲取需要掃描的包 */ private String[] retrievePackagesName(AnnotationMetadata annotationMetadata, AnnotationAttributes annotationAttributes) { String[] packages = annotationAttributes.getStringArray("packages"); if (packages.length > 0) { return packages; } //如果不存在,則默認第一個包開始 String className = annotationMetadata.getClassName(); return new String[]{className.split("\\.")[0]}; } @Override public void setEnvironment(Environment environment) { this.environment = environment; } @Override public void setResourceLoader(ResourceLoader resourceLoader) { this.resourceLoader = resourceLoader; } }
在啟動項中加入@EnableCustomImport注解配置
@SpringBootApplication @EnableCustomImport public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }