背景
我们都知道在我们最开始使用spring定义Bean的时候有如下方式
<bean id="testBean" class="com.zou.TestBean"/>
如果Bean多了我们不可能一个一个Bean标签去定义,就有了基于包去扫描
<context:component-scan base-package="com.zou"/>
后来流行注解编程后就将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 类结构

整个处理过程如下:
-
遍历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<String> basePackages = this.getBasePackages(importingClassMetadata);
scanner.scan(basePackages.toArray(new String[]{}));
}
protected Set<String> getBasePackages(AnnotationMetadata importingClassMetadata) {
Map<String, Object> attributes = importingClassMetadata
.getAnnotationAttributes(EnableHandle.class.getCanonicalName());
Set<String> 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);
}
}

可以看到我们的Bean都是正常的完成了注册
参考
博客
spring-cloud-openfeign
原文始发于微信公众号(小奏技术):Spring 自己实现一个自定义Bean注解注册器来惊艳面试官
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/30104.html