Component注解的派⽣性原理
1:模式注解
Stereotype Annotation俗称为模式注解。Spring核⼼部分提供了⼏种内建的模式注解,
如@Component,@Repository,@Service,@Controller,@Configuration等。这些注解均派⽣于@Component。
由于Java语⾔规定,Annotation不允许继承,没有类派⽣⼦类的特性,因此Spring采⽤元标注的⽅式实现注解之间的派⽣。
2:@Component派⽣性
@Component注解作为Spring容器托管的通⽤模式组件,任何被@Component标注的组件均为组件扫描的候选对象。
任何论证过程离不开所处的环境,需要开发⼈员具备⼀定⼯程意识,包括软件版本,特性范围,兼容情况等。因此,论证过程从最低版本开始推导,逐步证明不同版本得提升和差异。
3:@Component注解派⽣性原理
当ClassPathBeanDefinitionScanner# basePackages)调⽤时,它利⽤basePackages参数迭代执⾏
的findCandidateComponents(String basePackage),每次执⾏结果都⽣成候选的BeanDefinition集合,即candidates变量。
public class ClassPathBeanDefinitionScanner extends ClassPathScanningCandidateComponentProvider{
...
protected Set<BeanDefinitionHolder> basePackages) {
//获取候选的BeanDefinition集合
Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<BeanDefinitionHolder>();
for (String basePackage : basePackages) {
Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
...
}
return beanDefinitions;
}
...
}
⽽findCandidateComponents(String basePackage)从⽗类ClassPathScanningCandidateComponentProvider
中继承。
public class ClassPathScanningCandidateComponentProvider implements EnvironmentCapable, ResourceLoaderAware {
...
public Set<BeanDefinition> findCandidateComponents(String basePackage) {
Set<BeanDefinition> candidates = new LinkedHashSet<BeanDefinition>();
try {
//获取查询的package,并处理占位符情况${...},转换为ClassLoader资源(.class)搜索路径
String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
resolveBasePackage(basePackage) + '/' + sourcePattern;
Resource[] resources = Resources(packageSearchPath);
...
//resource迭代执⾏,当资源可读取时,获取该资源的MetadataReader对象
for (Resource resource : resources) {
.
..
if (resource.isReadable()) {
try {
//包含了类和注解元信息读取⽅法
MetadataReader metadataReader = MetadataReader(resource);
//判断资源是否为候选的组件,通过excludeFilters和includeFilters进⾏判断
if (isCandidateComponent(metadataReader)) {
//基于ASM,⽀持AnnotatedBeanDefinition接⼝
ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
sbd.setResource(resource);
sbd.setSource(resource);
/
/判断BeanDefinition是否候选组件
if (isCandidateComponent(sbd)) {
...
candidates.add(sbd);
}
...
}
}
...
return candidates;
}
.
..
/**
* Determine whether the given class does not match any exclude filter
* and does match at least one include filter.
* @param metadataReader the ASM ClassReader for the class
* @return whether the class qualifies as a candidate component
*/
protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException{
for (TypeFilter tf : ludeFilters) {
if (tf.match(metadataReader, adataReaderFactory)) {
return false;
}
}
for (TypeFilter tf : this.includeFilters) {
if (tf.match(metadataReader, adataReaderFactory)) {
return isConditionMatch(metadataReader);
}
}
return false;
}
/**
* Determine whether the given bean definition qualifies as candidate.
* <p>The default implementation checks whether the class is not an interface
* and not dependent on an enclosing class.
* <p>Can be overridden in subclasses.
* @param beanDefinition the bean definition to check
* @return whether the bean definition qualifies as a candidate component
*/
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
AnnotationMetadata metadata = Metadata();
return (metadata.isIndependent() && (metadata.isConcrete() ||
(metadata.isAbstract() && metadata.hasAnnotatedMethods(Name()))));
}
/
**
* Register the default filter for {@link Component @Component}.
* <p>This will implicitly register all annotations that have the
* {@link Component @Component} meta-annotation including the
* {@link Repository @Repository}, {@link Service @Service}, and
* {@link Controller @Controller} stereotype annotations.
* <p>Also supports Java EE 6's {@link javax.annotation.ManagedBean} and
* JSR-330's {@link javax.inject.Named} annotations, if available.
*
*/
@SuppressWarnings("unchecked")
protected void registerDefaultFilters() {
this.includeFilters.add(new AnnotationTypeFilter(Component.class));
ClassLoader cl = ClassLoader();
try {
this.includeFilters.add(new AnnotationTypeFilter(
((Class<? extends Annotation>) ClassUtils.forName("javax.annotation.ManagedBean", cl)), false));
...
}
try {
this.includeFilters.add(new AnnotationTypeFilter(
((Class<? extends Annotation>) ClassUtils.forName("javax.inject.Named", cl)), false));
.
..
}
}
}
默认情况下,ClassPathScanningCandidateComponentProvider构造参数useDefaultFilters为true,并且显⽰传递给⽗类构造参数。该⽅法给属性includeFilters增添了@Component类型AnnotationTypeFilter的TypeFilter。
ClassPathBeanDefinitionScanner默认过滤器引⼊标注@Component,@Repository,@Service或者@Controller等类。同理,它也能够标注所有@Component的"派⽣"注解。
@Component注解只包含⼀个value属性定义,所以其“派⽣”的注解也只能包含⼀个vlaue属性定义。
Dubbo实现@Service注解扫描实例:
ClassPathBeanDefinitionScanner允许⾃定义类型过滤规则。因此,Dubbo的@Service没有标注@Component情况下,通过
scanner.addIncludeFilter(new AnnotationTypeFilter(Service.class))⽅式达到识别@Service标注类情况。但是没有使
⽤@Component注解的派⽣性。
Mybatis实现@Mapper注解扫描实例:
public class ClassPathMapperScanner extends ClassPathBeanDefinitionScanner{
...
public ClassPathMapperScanner(BeanDefinitionRegistry registry) {
super(registry, false);
}
/**
* Configures parent scanner to search for the right interfaces. It can search
* for all interfaces or just for those that extends a markerInterface or/and
* those annotated with the annotationClass
*/
public void registerFilters() {
boolean acceptAllInterfaces = true;
// if specified, use the given annotation and / or marker interface
if (this.annotationClass != null) {
addIncludeFilter(new AnnotationTypeFilter(this.annotationClass));
acceptAllInterfaces = false;
}
// override AssignableTypeFilter to ignore matches on the actual marker interface
if (this.markerInterface != null) {
addIncludeFilter(new AssignableTypeFilter(this.markerInterface) {
@Override
protected boolean matchClassName(String className) {
return false;
}
});
acceptAllInterfaces = false;
}
if (acceptAllInterfaces) {
// default include filter that accepts all classes
addIncludeFilter(new TypeFilter() {
@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException { return true;
}
});
}
// exclude package-info.java
addExcludeFilter(new TypeFilter() {
@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException { String className = ClassMetadata().getClassName();
dsWith("package-info");
}
});
}
/**
* {@inheritDoc}
*/
@Override
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
Metadata().isInterface() && Metadata().isIndependent();
}
private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
GenericBeanDefinition definition;
for (BeanDefinitionHolder holder : beanDefinitions) {
definition = (GenericBeanDefinition) BeanDefinition();
...
//复杂对象构建考虑使⽤FactoryBean接⼝
// the mapper interface is the original class of the bean
// but, the actual class of the bean is MapperFactoryBean
//添加泛型参数
// issue #59
definition.setBeanClass(Class());
...
}
}
...
}
4:思考扩展
思考1:利⽤ClassPathBeanDefinitionScanner类配合includeFilters和excludeFilters定制化批量注册Bean到Spring容器中。常常可以通过注解⽅式来包含或者排除候选类。
TypeFilter常⽤实现
AnnotationTypeFilter:注解类型过滤器
AssignableTypeFilter:确定此对象表⽰的类或者接⼝是否为给定类或者接⼝相同。
RegexPatternTypeFilter:判断给定的类名是否符合指定正则表达式。
思考2:复杂对象构建考虑使⽤FactoryBean实现类。
思考3:如果是读取类和注解信息可以考虑基于ASM或者反射,使⽤⽅式往下可以获取。当获取已加载的类信息可以考虑反射(反射⼤前提是被反射的Class被ClassLoader加载),ASM⽤于不需要将类路径package下的Class全部加载,Spring应⽤指定Java package扫描Spring模式注解时,利⽤的就是基于ASM⽅式获取类或者注解信息。基于ASM获取会获得更⼤性能。
思考4:资源读取考虑使⽤ResourcePatternResolver,这个对象的获取可以通过Spring提供的⼯具类
占位符${...}的情况,注意资源是否可读。
5:多层次@Component派⽣性
(1):具体发展过程不再细说,详解请看SpringBoot编程思想这本书。其多层次@Component注解派⽣性构建在Spring4.x。其核⼼处理类
为AnnotationMetadataReadingVisitor,其采⽤递归的⽅式查元注解。
(2):Spring中,MetadataReader接⼝唯⼀实现⾮公开类SimpleMetadataReader。可以通过SimpleMetadataReaderFactory(ASM字节码操作)和CachingMetadataReaderFactory获取。
其中在SimpleMetadataReader实现上看,ClassMetadataReadingVisitor和AnnotationMetadataReadingVisitor分别springboot 原理解析
是ClassMetadatta和AnnotationMetadata实现类。
由于ClassPathBeanDefinitionScanner在寻候选的BeanDefinition过程中,将指定basePackage参数下
的*.class资源进⾏元信息解析,也就是ClassMetadata和AnnotationMetadata对象。
AnnotationMetadataReadingVisitor实现上使⽤了AnnotationAttributesReadingVisitor,该类主要实现⽅法是visitEnd()。Spring2.5实现未采⽤层次递归获取Annotation[],所以仅⽀持单层次的@Component派⽣。Spring3.x实现仅两层@Component派⽣。Spring4.x开始采⽤递归⽅式查元注解。相关实现在Spring为公开类AnnotationAttributesReadingVisitor。
final class AnnotationAttributesReadingVisitor extends RecursiveAnnotationAttributesVisitor {
private final MultiValueMap<String, AnnotationAttributes> attributesMap;
private final Map<String, Set<String>> metaAnnotationMap;
...
@Override
public void visitEnd() {
super.visitEnd();
Class<?> annotationClass = this.attributes.annotationType();
if (annotationClass != null) {
List<AnnotationAttributes> attributeList = (this.annotationType);
if (attributeList == null) {
this.attributesMap.add(this.annotationType, this.attributes);
}
else {
attributeList.add(0, this.attributes);
}
Set<Annotation> visited = new LinkedHashSet<Annotation>();
Annotation[] metaAnnotations = Annotations(annotationClass);
if (!ObjectUtils.isEmpty(metaAnnotations)) {
for (Annotation metaAnnotation : metaAnnotations) {
if (!AnnotationUtils.isInJavaLangAnnotationPackage(metaAnnotation)) {
recursivelyCollectMetaAnnotations(visited, metaAnnotation);
}
}
}
if (aAnnotationMap != null) {
Set<String> metaAnnotationTypeNames = new LinkedHashSet<String>(visited.size());
for (Annotation ann : visited) {
metaAnnotationTypeNames.add(ann.annotationType().getName());
}
}
}
}
private void recursivelyCollectMetaAnnotations(Set<Annotation> visited, Annotation annotation) {
Class<? extends Annotation> annotationType = annotation.annotationType();
String annotationName = Name();
if (!AnnotationUtils.isInJavaLangAnnotationPackage(annotationName) && visited.add(annotation)) {
try {
// Only do attribute scanning for public annotations; we'd run into
// IllegalAccessExceptions otherwise, and we don't want to mess with
// accessibility in a SecurityManager environment.
if (Modifier.Modifiers())) {
this.attributesMap.add(annotationName,
}
//实现递归查元注解
for (Annotation metaMetaAnnotation : Annotations()) {
recursivelyCollectMetaAnnotations(visited, metaMetaAnnotation);
}
}
catch (Throwable ex) {
if (logger.isDebugEnabled()) {
logger.debug("Failed to introspect meta-annotations on [" + annotation + "]: " + ex);
}
}
}
}
}
(3):思考扩展
考虑使⽤ASM的⽅式读取类或者注解相关信息。(不需要全部将指定路径下的类加载)
MetadataReaderFactory:获取MetadataReader⼯⼚
SimpleMetadataReaderFactory:简单获取MetadataReader⼯⼚实现
ClassReader:基于ASM读取类相关信息,公开类,不建议单独使⽤。
AnnotationMetadataReadingVisitor:基于ASM读取注解元数据相关信息,不建议单独使⽤。
MethodMetadataReadingVisitor:基于ASM读取⽅法相关信息,不建议单独使⽤。
CachingMetadataReaderFactory:继承SimpleMetadataReaderFactory,增加缓存MetadataReader资源功能。
MetadataReader:获取访问类和注解相关信息。通过MetadataReaderFactory获取。
Resource getResource():获取类⽂件资源引⽤
ClassMetadata getClassMetadata():读取基础类的基本元数据
AnnotationMetadata getAnnotationMetadata():读取底层类完整注解元数据,包含注解⽅法的注解元数据。
考虑使⽤反射的⽅式读取类或者注解相关信息(⽐较费时⽽且该类必须被ClassLoader加载)
StandardClassMetadata:基于反射读取类元数据,可建议单独使⽤。
StandardAnnotationMetadata:基于反射读取注解元数据,可建议单独使⽤
StandardMethodMetadata:基于反射读取⽅法元数据,可建议单独使⽤
考虑使⽤Spring内部⽀持的有⽤⼯具类,都是来⾃于spring-core包中。多使⽤spring内建API,学习他们的长处。
ClassUtils:类⼯具类
CollectionUtils:集合⼯具类
NumberUtils:Number⼯具类
MimeTypeUtils:媒体类型⼯具类
IdGenerator:Id⽣成器
StringUtils:字符串⼯具类
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论