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