spring自身经历了快速的发展, 我印象中还是古板的xml配置, 而现在已经完全不需要xml了… 直接注解搞定确实给开发者省了很多工作. 本文是在阅读
spring的官方关于注解模型:
https://github.com/spring-projects/spring-framework/wiki/Spring-Annotation-Programming-Model
简单来说注解模型主要覆盖如下话题:
本文主要会覆盖前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版本的注解是支持单程继承/派生的.
我们上面的注解栈:
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
比如下面这样的: 代码:
SecondLevelRepository
| - FirstLevelRepository
| - Repository
| - Component
运行HierarchicalDerivedComponentAnnotationBootstrap
发现在4.0.0才开始支持这种多层继承注解. 通过递归调用实现AnnotationAttributesReadingVisitor
spring复杂的注解机制给后面的springboot提供了很好的基础. 包括复杂的组合注解也得到了了很好的支持