赞
踩
想必@Component
注解大家一直在使用,只要类上加上它,就可以被Spring容器管理,那大家有想过它是怎么实现的吗?本篇文章就带领到家揭秘。
用来标记的类是一个“组件”或者说是一个Bean,Spring会自动扫描标记@Component
注解的类作为一个Spring Bean对象。
注解源码:
- @Target(ElementType.TYPE)
- @Retention(RetentionPolicy.RUNTIME)
- @Documented
- @Indexed
- public @interface Component {
-
- /**
- * The value may indicate a suggestion for a logical component name,
- * to be turned into a Spring bean in case of an autodetected component.
- * @return the suggested component name, if any (or empty String otherwise)
- */
- String value() default "";
-
- }
- 复制代码
属性说明:
元注解说明:
@Component
注解修饰小结: 通过添加@Component
能够将类转为Spring中的Bean对象,前提是能过够被扫描到。
阅读源码,我们查看@Component
注解的源码,从中可以看到一个关键的类ClassPathBeanDefinitionScanner
,我们可以从这个类下手,找到切入点。
分析ClassPathBeanDefinitionScanner
类,找到核心方法doscan
, 打个断点,了解整个调用链路。
具体分析结果如下:
ConfigurationClassPostProcessor
这个Bean,它实现了BeanDefinitionRegistryPostProcessor
接口,而这个接口是Spring提供的一个扩展点,可以往BeanDefinition Registry中添加BeanDefintion。所以,只要能够扫描到@Component
注解的类,并且把它注册到BeanDefinition Registry中即可。ConfigurationClassPostProcessor
的postProcessBeanDefinitionRegistry
,查找@Component
的类,并进行注册。@Component
的类的,核心方法就是ClassPathBeanDefinitionScanner#doScan
。- protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
- Assert.notEmpty(basePackages, "At least one base package must be specified");
- Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
- // 遍历多个扫描目录,如本例中的com.alvinlkk
- for (String basePackage : basePackages) {
- // 核心方法查找所有符合条件的BeanDefinition, 该方法后面重点关注
- Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
- // 遍历找到的BeanDefinition
- for (BeanDefinition candidate : candidates) {
- ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
- candidate.setScope(scopeMetadata.getScopeName());
- String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
- if (candidate instanceof AbstractBeanDefinition) {
- postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
- }
- if (candidate instanceof AnnotatedBeanDefinition) {
- AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
- }
- // 验证BeanDefinition
- if (checkCandidate(beanName, candidate)) {
- BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
- definitionHolder =
- AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
- beanDefinitions.add(definitionHolder);
- // 注册BeanDefinition到registry中
- registerBeanDefinition(definitionHolder, this.registry);
- }
- }
- }
- return beanDefinitions;
- }
- 复制代码
ClassPathBeanDefinitionScanner#findCandidateComponents
方法,找出候选的Bean Component。- public Set<BeanDefinition> findCandidateComponents(String basePackage) {
- // 判断组件是否加了索引,打包后默认会有索引,用于加快扫描
- if (this.componentsIndex != null && indexSupportsIncludeFilters()) {
- return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);
- }
- // 重点查看else逻辑
- else {
- return scanCandidateComponents(basePackage);
- }
- }
- 复制代码
- private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
- Set<BeanDefinition> candidates = new LinkedHashSet<>();
- try {
- // 解析出需要扫描的路径,本例是classpath*:com/alvinlkk/**/*.class
- String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
- resolveBasePackage(basePackage) + '/' + this.resourcePattern;
- // 根据扫描路径找到所有的Resource
- Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
- boolean traceEnabled = logger.isTraceEnabled();
- boolean debugEnabled = logger.isDebugEnabled();
- // 遍历扫描路径
- for (Resource resource : resources) {
- if (traceEnabled) {
- logger.trace("Scanning " + resource);
- }
- try {
- // 解析出扫描到类的元数据信息,里面包含了注解信息
- MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
- // 关键方法,判断是否候选组件
- if (isCandidateComponent(metadataReader)) {
- ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
- sbd.setSource(resource);
- if (isCandidateComponent(sbd)) {
- if (debugEnabled) {
- logger.debug("Identified candidate component class: " + resource);
- }
- candidates.add(sbd);
- }
- else {
- if (debugEnabled) {
- logger.debug("Ignored because not a concrete top-level class: " + resource);
- }
- }
- }
- else {
- if (traceEnabled) {
- logger.trace("Ignored because not matching any filter: " + resource);
- }
- }
- }
- catch (FileNotFoundException ex) {
- if (traceEnabled) {
- logger.trace("Ignored non-readable " + resource + ": " + ex.getMessage());
- }
- }
- catch (Throwable ex) {
- throw new BeanDefinitionStoreException(
- "Failed to read candidate component class: " + resource, ex);
- }
- }
- }
- catch (IOException ex) {
- throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);
- }
- return candidates;
- }
- 复制代码
- // 判断是否候选的Bean Component
- protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
- // exclude过滤器,在exclude过滤其中的,会直接排除掉,返回false
- for (TypeFilter tf : this.excludeFilters) {
- if (tf.match(metadataReader, getMetadataReaderFactory())) {
- return false;
- }
- }
- // include过滤器, 这里会看到有AnnotationTypeFilter,注解类型过滤器
- for (TypeFilter tf : this.includeFilters) {
- // 调用AnnotationTypeFilter的match方法,来判断是否满足条件
- if (tf.match(metadataReader, getMetadataReaderFactory())) {
- // 下面在进行Condition的判断,就是类上的@Conditional,这里不是重点
- return isConditionMatch(metadataReader);
- }
- }
- return false;
- }
- 复制代码
而这个AnnotationTypeFilter默认是在构造函数中注册进去的。
小结:
@Component到Spring bean容器管理过程如下:
经过这篇文章文章,是不是对@Component的使用和实现原理一清二楚了呢,其实Spring中还有@Service、@Controller和@Repository等注解,他们和@Component有什么区别呢?
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。