文章目录
- 背景
- Spring内置扫描器
- 实战
- 自定义Bean注解
- 自定义 注解扫描器
- 自定义Bean注册处理器
- 测试
- 单Bean注入
- 依赖注入
- 接口注入
- 运行结果
- 参考
背景
我们都知道在我们最开始使用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都是正常的完成了注册
参考