丛林探险之Spring自定义注解加载Bean

Nerissa ·
更新时间:2024-11-10
· 861 次阅读

文章目录丛林背景角色(一)自定义注解 StrategyBean(二)自定义注解 StrategyBeanScan(三)自定义注册机 StrategyBeanScanRegistrar(四)自定义Bean工厂后置处理器StrategyBeanAnnotationBeanPostProcessor时序图源码地址 丛林背景

自定义注解加载Bean是Spring框架提供的一个扩展点。基于这个扩展点可以实现灵活加载Bean的功能。

例如 Dubbo框架通过这个扩展点将添加了自定义注解@org.apache.dubbo.config.annotation.Service和@org.apache.dubbo.config.annotation.Reference的类加载到Spring的Ioc容器中。

今天我们像Dubbo框架一样自定义注解和必要的处理类来演绎自定义注解加载到Ioc容器的过程。

角色 (一)自定义注解 StrategyBean /** * 自定义注解 * 添加该自定义注解的类会被动态添加到Ioc容器 */ @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface StrategyBean { String value(); } @StrategyBean("beijing") public class BeijingStrategy { public BeijingStrategy(){ System.out.println("BeijingStrategy Created"); } } @StrategyBean("hangzhou") public class HangzhouStrategy { public HangzhouStrategy(){ System.out.println("HangzhouStrategy Created"); } } (二)自定义注解 StrategyBeanScan

这个类有两个功能
1) 组合了了一个注解 @Import(StrategyBeanScanRegistrar.class),这个Import注解会将StrategyBeanScanRegistrar加载到Ioc容器中,Ioc容器会在后续调用加载进来的StrategyBeanScanRegistrar的registerBeanDefinitions方法
2) 配置要扫描的包路径

这个注解可以添加在启动类上

@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Import(StrategyBeanScanRegistrar.class) public @interface StrategyBeanScan { String[] basePackages() default {}; Class[] basePackageClasses() default {}; String[] value() default {}; } @StrategyBeanScan(basePackages = {"com.liuapi.incubator.repository"}) public class RepositoryApplication { public static void main(String[] args) { ConfigurableApplicationContext context = SpringApplication.run(RepositoryApplication.class); } } (三)自定义注册机 StrategyBeanScanRegistrar

StrategyBeanScanRegistrar实现了ImportBeanDefinitionRegistrar 接口,他的职责就是将工厂后置处理器StrategyBeanAnnotationBeanPostProcessor加载到Ioc容器中

/** * 该注册机的职责为 将工厂后置处理器StrategyBeanAnnotationBeanPostProcessor加载到Ioc容器中 */ public class StrategyBeanScanRegistrar implements ImportBeanDefinitionRegistrar { @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry, BeanNameGenerator importBeanNameGenerator) { // 获取注解中配置的要扫描的包名集合 Set packagesToScan = getPackagesToScan(importingClassMetadata); // 将Bean工厂后置处理器架加载到Ioc容器 BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.rootBeanDefinition(StrategyBeanAnnotationBeanPostProcessor.class); beanDefinitionBuilder.addConstructorArgValue(packagesToScan); AbstractBeanDefinition beanDefinition = beanDefinitionBuilder.getBeanDefinition(); BeanDefinitionReaderUtils.registerWithGeneratedName(beanDefinition, registry); } @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { } private Set getPackagesToScan(AnnotationMetadata metadata) { AnnotationAttributes attributes = AnnotationAttributes.fromMap( metadata.getAnnotationAttributes(StrategyBeanScan.class.getName())); String[] basePackages = attributes.getStringArray("basePackages"); Class[] basePackageClasses = attributes.getClassArray("basePackageClasses"); String[] value = attributes.getStringArray("value"); // Appends value array attributes Set packagesToScan = new LinkedHashSet(Arrays.asList(value)); packagesToScan.addAll(Arrays.asList(basePackages)); for (Class basePackageClass : basePackageClasses) { packagesToScan.add(ClassUtils.getPackageName(basePackageClass)); } if (packagesToScan.isEmpty()) { return Collections.singleton(ClassUtils.getPackageName(metadata.getClassName())); } return packagesToScan; } } (四)自定义Bean工厂后置处理器StrategyBeanAnnotationBeanPostProcessor

这个Bean工厂后置处理器的职责就是扫描指定包下添加了自定义注解StrategyBean的类,并将这些类动态加载到Ioc容器中

/** * Note: 需要将该Bean工厂后置处理器导入到 Ioc容器中,需要借助@Import导入的注册机 * Description: 该工厂后置处理器会扫描指定包下添加了自定义注解StrategyBean的类,并将这些类动态加载到Ioc容器中 */ @Slf4j public class StrategyBeanAnnotationBeanPostProcessor implements BeanDefinitionRegistryPostProcessor, EnvironmentAware, ResourceLoaderAware { private final Set packagesToScan; private Environment environment; private ResourceLoader resourceLoader; public StrategyBeanAnnotationBeanPostProcessor(Collection packagesToScan) { this.packagesToScan = new LinkedHashSet(packagesToScan); } @Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException { ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(registry, false, environment, resourceLoader); scanner.addIncludeFilter(new AnnotationTypeFilter(StrategyBean.class)); if (CollectionUtils.isEmpty(packagesToScan)) { return; } packagesToScan.stream() .forEach( packageToScan -> { int scan = scanner.scan(packageToScan); if (0 == scan) { log.warn("packagesToScan is empty , {} registry will be ignored!", StrategyBean.class.getSimpleName()); } else { log.info("Load {} bean with Annotation {} in package {}", scan, StrategyBean.class.getSimpleName(), packageToScan); } } ); } @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; } } 时序图

原创文章 9获赞 12访问量 1169 关注 私信 展开阅读全文
作者:JOHN!



注解 spring bean

需要 登录 后方可回复, 如果你还没有账号请 注册新账号