丛林背景
自定义注解加载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<String> 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<String> 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<String> packagesToScan = new LinkedHashSet<String>(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<String> packagesToScan;
private Environment environment;
private ResourceLoader resourceLoader;
public StrategyBeanAnnotationBeanPostProcessor(Collection<String> 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;
}
}
时序图
转载:https://blog.csdn.net/qq_35695616/article/details/105985838
查看评论