Spring 自己实现一个自定义Bean注解注册器来惊艳面试官

您所在的位置:网站首页 spring注解扫描器源码 Spring 自己实现一个自定义Bean注解注册器来惊艳面试官

Spring 自己实现一个自定义Bean注解注册器来惊艳面试官

2023-07-02 14:45| 来源: 网络整理| 查看: 265

背景

我们都知道在我们最开始使用spring定义Bean的时候有如下方式

复制代码

如果Bean多了我们不可能一个一个Bean标签去定义,就有了基于包去扫描

复制代码

后来流行注解编程后就将xml改为@ComponentScan注解了,然后配置@Configuration注解一起使用

@ComponentScan(basePackages = {"com.zou"}) public class Application { ... } 复制代码

但是不管使用上面哪一种方式,他扫描的都只是Spring 内部定义的一些Bean注册注解,比如@Component、@Service、@Controller、@Repository等。但有时候我们需要自定义一些注解来区分这些Bean的作用,比如我这边想定义一些事件处理的Bean,自定义一个注解(Handle)来区分他们和普通的Bean区分

Spring内置扫描器

目前Spring主要的Bean扫描器有两个

ClassPathBeanDefinitionScanner:component-scan标签底层底层实现 ComponentScanAnnotationParser:@ComponentScan注解配合@Configuration注解底层实现

我们这里简单看看ClassPathBeanDefinitionScanner的处理过程

ClassPathBeanDefinitionScanner 类结构

image-20220119103313832

整个处理过程如下:

遍历basePackages,根据每个basePackage找出这个包下的所有的class 遍历找到的Resource集合,通过includeFilters和excludeFilters判断是否解析。这里的includeFilters和excludeFilters是TypeFilter接口类型的集合,是ClassPathBeanDefinitionScanner内部的属性。TypeFilter接口是一个用于判断类型是否满足要求的类型过滤器。excludeFilters中只要有一个TypeFilter满足条件,这个Resource就会被过滤。includeFilters中只要有一个TypeFilter满足条件,这个Resource就不会被过滤 如果没有被过滤。把Resource封装成ScannedGenericBeanDefinition添加到BeanDefinition结果集中 返回最后的BeanDefinition结果集

这里就不过多深入研究原理了,我们以实战为主

实战 自定义Bean注解

首先我们肯定需要一个注解Handle

Handle @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Handle { } 复制代码

接下来就需要知道如何去扫描到加了这些注解的Bean,并注册到Spring容器中

自定义 注解扫描器 EnableHandle @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Import({HandleRegistrar.class}) public @interface EnableHandle { String[] value() default {}; String[] basePackages() default {}; Class[] basePackageClasses() default {}; } 复制代码 自定义Bean注册处理器 HandleRegistrar public class HandleRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware { private ResourceLoader resourceLoader; private Environment environment; @Override public void setResourceLoader(ResourceLoader resourceLoader) { this.resourceLoader = resourceLoader; } @Override public void setEnvironment(Environment environment) { this.environment = environment; } @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { ClassPathBeanDefinitionScanner scanner = getScanner(registry); scanner.setResourceLoader(this.resourceLoader); scanner.addIncludeFilter(new AnnotationTypeFilter(Handle.class)); Set basePackages = this.getBasePackages(importingClassMetadata); scanner.scan(basePackages.toArray(new String[]{})); } protected Set getBasePackages(AnnotationMetadata importingClassMetadata) { Map attributes = importingClassMetadata .getAnnotationAttributes(EnableHandle.class.getCanonicalName()); Set basePackages = new HashSet(); for (String pkg : (String[]) attributes.get("value")) { if (StringUtils.hasText(pkg)) { basePackages.add(pkg); } } for (String pkg : (String[]) attributes.get("basePackages")) { if (StringUtils.hasText(pkg)) { basePackages.add(pkg); } } for (Class clazz : (Class[]) attributes.get("basePackageClasses")) { basePackages.add(ClassUtils.getPackageName(clazz)); } if (basePackages.isEmpty()) { basePackages.add(ClassUtils.getPackageName(importingClassMetadata.getClassName())); } return basePackages; } protected ClassPathBeanDefinitionScanner getScanner(BeanDefinitionRegistry registry) { return new ClassPathBeanDefinitionScanner(registry, false, this.environment, this.resourceLoader) { @Override protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) { boolean isCandidate = false; if (beanDefinition.getMetadata().isIndependent()) { if (!beanDefinition.getMetadata().isAnnotation()) { isCandidate = true; } } return isCandidate; } }; } } 复制代码

核心的Bean注册都交给了Spring,即代码 super.doScan(basePackages);

注意 getScanner()方法中获取了 ClassPathBeanDefinitionScanner类,并重写了isCandidateComponent 方法,isCandidateComponent是否成立的条件是beanDefinitions对应的类是top class或者nested class且不是注解

测试 单Bean注入 @Handle public class TestBean { private Integer i; public TestBean() { this.i = 2; System.out.println("单Bean 注册"); } public Integer getI() { return i; } } 复制代码 依赖注入 @Handle public class TestABean { private TestBean testBean; public TestABean(TestBean testBean) { System.out.println("依赖Bean加载"); this.testBean = testBean; System.out.println(this.testBean.getI()); } } 复制代码 接口注入 public interface TestInterface { } 复制代码 @Handle public class TestInterfaceImpl implements TestInterface{ public TestInterfaceImpl() { System.out.println("测试接口实现"); } } 复制代码 运行结果 @SpringBootApplication @EnableHandle public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } 复制代码

image-20220119135743135

可以看到我们的Bean都是正常的完成了注册

参考

博客

spring-cloud-openfeign

觉得文章不错欢迎关注公众号:小奏技术


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3