[Springboot编程思想]ch7-spring的组合注解

Abstract

spring自身经历了快速的发展, 我印象中还是古板的xml配置, 而现在已经完全不需要xml了… 直接注解搞定确实给开发者省了很多工作. 本文是在阅读<springboot编程思想>过程中的记录

spring的注解编程模型

spring的官方关于注解模型:

https://github.com/spring-projects/spring-framework/wiki/Spring-Annotation-Programming-Model

简单来说注解模型主要覆盖如下话题:

  • 元注解 meta annotation
  • 模式注解 stereotype annotation
  • 组合注解 composed annotation
  • 属性别名和覆盖

本文主要会覆盖前3个话题.

元注解

注解别的注解的注解. 比如 spring中的@Component 就是元注解… 这个注解在 @Service @Repository上都有.
(spring v5.1.2)
在这里插入图片描述

模式注解

模式注解主要用于描述在应用中某种角色的注解. 比如@Service 是一个服务, @Repository是DAO. 还有@Controller , @RestController.
由于java的注解本身不具有继承性. 但是spring的注解是具有继承/派生特性的. 下面会具体解释.

体验注解的派生性

spring版本2.5.6 使用这个2.5.6example:
这个例子中我们自己创建一个注解(继承自Component):

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component // 测试多层次 @Component派生,请将当前注释
//@Repository // 测试多层次 @Component派生,请将当前反注释,并且将 spring-context 升级到 3.0.0.RELEASE
public @interface StringRepository {

运行代码DerivedComponentAnnotationBootstrap:

nameRepository.findAll() = [张三, 李四, 小马哥]

发现自己写的StringRepository继承了component的特性被spring加载识别.

context.refresh
  -> XmlBeanDefinitionReader#doLoadBeanDefinitions
     -> ComponentScanBeanDefinitionParser#parse
       -> ClassPathScanningCandidateComponentProvider#findCandidateComponents
          -> ClassPathScanningCandidateComponentProvider#isCandidateComponent

	protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
		for (TypeFilter tf : this.excludeFilters) {
			if (tf.match(metadataReader, this.metadataReaderFactory)) {
				return false;
			}
		}
		for (TypeFilter tf : this.includeFilters) {
			if (tf.match(metadataReader, this.metadataReaderFactory)) {
				return true;
			}
		}
		return false;
	}          

默认的includeFilters 为: this.includeFilters.add(new AnnotationTypeFilter(Component.class)); (只识别Componet class)
那么我们的StringRepository是怎么被加入的呢?

SimpleMetadataReader#getAnnotationMetadata  
 -> AnnotationMetadataReadingVisitor#visitAnnotation
   -> org.objectweb.asm.commons.EmptyVisitor#visitEnd

在遍历完成后: AnnotationMetadataReadingVisitor中的放入了对应的annotation:
在这里插入图片描述然后在上面的typefilter中的match就会判断:

-- 判断自己的注解(attributesMap) || 判断自己的元注解有没有 (metaMetaAnnotationMap)

	@Override
	protected boolean matchSelf(MetadataReader metadataReader) {
		AnnotationMetadata metadata = metadataReader.getAnnotationMetadata();
		return metadata.hasAnnotation(this.annotationType.getName()) || 
				(this.considerMetaAnnotations && metadata.hasMetaAnnotation(this.annotationType.getName()));
	}

在这里插入图片描述所以这里可以看到2.5.6版本的注解是支持单程继承/派生的.

spring的多继承注解模型

我们上面的注解栈:

 StringRepository
      | -  Component

我们试试多层的:

 StringRepository
      | -  Repository
          | - Component 

我们修改上面的自定义注解:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
//@Component // 测试多层次 @Component派生,请将当前注释
@Repository // 测试多层次 @Component派生,请将当前反注释,并且将 spring-context 升级到 3.0.0.RELEASE
public @interface StringRepository {

发现运行失败, 2.5.6不支持多层注解.

Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'nameRepositoryHolder': Autowiring of fields failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private java.util.Collection thinking.in.spring.boot.samples.spring25.repository.AutowiredBeanHolder.repositories; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No matching bean of type [thinking.in.spring.boot.samples.spring25.repository.NameRepository] found for dependency [collection of thinking.in.spring.boot.samples.spring25.repository.NameRepository]: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
	at org.springframework.beans.facto

尝试后, 发现从3.0.0开始支持. 所以使用代码3.0.0example:
最终发现他们的区别在(注意看这里发现了2个注解component和repository)
在这里插入图片描述原因就是AnnotationAttributesReadingVisitor也会去读取meta信息

org.springframework.core.type.classreading.AnnotationAttributesReadingVisitor#visitEnd

在这里插入图片描述之前的2.5.6:一个for循环
在这里插入图片描述

那么如果是多层继承呢?

比如下面这样的: 代码:

SecondLevelRepository
   | -  FirstLevelRepository
         | - Repository
             | - Component

运行HierarchicalDerivedComponentAnnotationBootstrap 发现在4.0.0才开始支持这种多层继承注解. 通过递归调用实现AnnotationAttributesReadingVisitor
在这里插入图片描述

总结

spring复杂的注解机制给后面的springboot提供了很好的基础. 包括复杂的组合注解也得到了了很好的支持

猜你喜欢

转载自blog.csdn.net/scugxl/article/details/106539814