[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提供了很好的基礎. 包括複雜的組合註解也得到了了很好的支持

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章