Spring源码之注解的原理

https://blog.csdn.net/qq_28802119/article/details/83573950
https://www.zhihu.com/question/318439660/answer/639644735
https://blog.csdn.net/u014534808/article/details/81071452
https://www.cnblogs.com/lxyit/p/10210581.html
https://www.cnblogs.com/lipengsheng-javaweb/p/12888842.html
https://www.zhihu.com/question/24401191
https://www.cnblogs.com/yangming1996/p/9295168.html(最好的一篇)
https://docs.oracle.com/javase/specs/jls/se8/html/jls-9.html#jls-9.6(官网)

JDK注解原理

原理总结:所有的注解都继承了Annotation接口,而注解类什么的注解,属于类中attribute。被注解修饰的类,在运行时会生成一个代理类(RetentionPolicy.RUNTIME),重写继承而来所有方法,从而实现功能。

所有注解都继承了annotation,打开任意注解类,用jclasslib查看便能看到:

Access flags: 0x... [public interface abstract annotation]

而在这个继承Annotation的注解类上面的注解,则属于这个类的Attributes

属性表有以下几种:

RuntimeVisibleAnnotations:运行时可见的注解
RuntimeInVisibleAnnotations:运行时不可见的注解
RuntimeVisibleParameterAnnotations:运行时可见的方法参数注解
RuntimeInVisibleParameterAnnotations:运行时不可见的方法参数注解
AnnotationDefault:注解类元素的默认值

所以在Class.forName(name, false, clToUse)进行反射时,便能获取此类上面annotation属性

而当要去获取一个注解类实例时(如调用getAnnotation),便会生成一个代理类。重写其所有方法,包括 value 方法以及自定义接口从 Annotation 接口继承而来的方法

自定义注解类:

import org.springframework.stereotype.Component;
import java.lang.annotation.*;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface MyComponent {
}

测试类:

@MyComponent
public class AnnotationTest {
    public static void main(String[] args) {
        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
        MyComponent annotation = AnnotationTest.class.getAnnotation(MyComponent.class);
        System.out.println(annotation.annotationType());
    }
}

生成的代理类:

public final class $Proxy0 extends Proxy implements Retention {
    ...

    public final Class annotationType() throws  {
        try {
            return (Class)super.h.invoke(this, m4, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final RetentionPolicy value() throws  {
        try {
            return (RetentionPolicy)super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    ...
}

public final class $Proxy1 extends Proxy implements MyComponent {
    ...

    public final String test() throws  {
        try {
            return (String)super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final Class annotationType() throws  {
        try {
            return (Class)super.h.invoke(this, m4, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    ...
}

@TODO: 为何生成两个代理类

Spring中注解

createApplicationContext()时registerDefaultFilters

调用链:

SpringApplication#run() --> SpringApplication#createApplicationContext() --> new AnnotationConfigServletWebServerApplicationContext() --> ClassPathBeanDefinitionScanner# --> ClassPathScanningCandidateComponentProvider#registerDefaultFilters()

在registerDefaultFilters中会注册默认两个includerFilters(Component和ManagedBean)

protected void registerDefaultFilters() {
	this.includeFilters.add(new AnnotationTypeFilter(Component.class));
	ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader();
	try {
		this.includeFilters.add(new AnnotationTypeFilter(
				((Class<? extends Annotation>) ClassUtils.forName("javax.annotation.ManagedBean", cl)), false));
		logger.trace("JSR-250 'javax.annotation.ManagedBean' found and supported for component scanning");
	}
	......
}

同样断点,可查看到会将ComponentScanAnnotationParser、AutoConfigurationExcludeFilter、TypeExcludeFilter加入excludeFilters

获取MetadataReader

调用链:

AbstractApplicationContext#refresh() --> AbstractApplicationContext#invokeBeanFactoryPostProcessors() --> PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors() --> PostProcessorRegistrationDelegate#invokeBeanDefinitionRegistryPostProcessors() --> ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry()--> ConfigurationClassPostProcessor#processConfigBeanDefinitions() --> ConfigurationClassPostProcessor#parse() --> ConfigurationClassPostProcessor#processConfigurationClass() -->
ConfigurationClassParser#parse() -->
ConfigurationClassParser#doProcessConfigurationClass() -->
ComponentScanAnnotationParser#parse() --> ClassPathBeanDefinitionScanner#doScan() --> ClassPathScanningCandidateComponentProvider#findCandidateComponents() --> ClassPathScanningCandidateComponentProvider#scanCandidateComponents --> CachingMetadataReaderFactory#getMetadataReader() --> SimpleMetadataReaderFactory#getMetadataReader()--> SimpleMetadataReader# -->ClassReader#accept()

在ClassReader类中依赖ASM字节码库实现,通过反射获取类所有信息。注解信息在类的attribute中

/**
* Makes the given visitor visit the JVMS ClassFile structure passed to the constructor of this
* {@link ClassReader}.
*
* @param classVisitor the visitor that must visit this class.
* @param attributePrototypes prototypes of the attributes that must be parsed during the visit of
*     the class. Any attribute whose type is not equal to the type of one the prototypes will not
*     be parsed: its byte array value will be passed unchanged to the ClassWriter. <i>This may
*     corrupt it if this value contains references to the constant pool, or has syntactic or
*     semantic links with a class element that has been transformed by a class adapter between
*     the reader and the writer</i>.
* @param parsingOptions the options to use to parse this class. One or more of {@link
*     #SKIP_CODE}, {@link #SKIP_DEBUG}, {@link #SKIP_FRAMES} or {@link #EXPAND_FRAMES}.
*/
@SuppressWarnings("deprecation")
public void accept(
  final ClassVisitor classVisitor,
  final Attribute[] attributePrototypes,
  final int parsingOptions) {
......

// Visit the RuntimeVisibleAnnotations attribute.
if (runtimeVisibleAnnotationsOffset != 0) {
  int numAnnotations = readUnsignedShort(runtimeVisibleAnnotationsOffset);
  int currentAnnotationOffset = runtimeVisibleAnnotationsOffset + 2;
  while (numAnnotations-- > 0) {
    // Parse the type_index field.
    String annotationDescriptor = readUTF8(currentAnnotationOffset, charBuffer);
    currentAnnotationOffset += 2;
    // Parse num_element_value_pairs and element_value_pairs and visit these values.
    currentAnnotationOffset =
        readElementValues(
            classVisitor.visitAnnotation(annotationDescriptor, /* visible = */ true),
            currentAnnotationOffset,
            /* named = */ true,
            charBuffer);
  }
}

// Visit the RuntimeVisibleTypeAnnotations attribute.
if (runtimeVisibleTypeAnnotationsOffset != 0) {
  int numAnnotations = readUnsignedShort(runtimeVisibleTypeAnnotationsOffset);
  int currentAnnotationOffset = runtimeVisibleTypeAnnotationsOffset + 2;
  while (numAnnotations-- > 0) {
    // Parse the target_type, target_info and target_path fields.
    currentAnnotationOffset = readTypeAnnotationTarget(context, currentAnnotationOffset);
    // Parse the type_index field.
    String annotationDescriptor = readUTF8(currentAnnotationOffset, charBuffer);
    currentAnnotationOffset += 2;
    // Parse num_element_value_pairs and element_value_pairs and visit these values.
    currentAnnotationOffset =
        readElementValues(
            classVisitor.visitTypeAnnotation(
                context.currentTypeAnnotationTarget,
                context.currentTypeAnnotationTargetPath,
                annotationDescriptor,
                /* visible = */ true),
            currentAnnotationOffset,
            /* named = */ true,
            charBuffer);
  }
}

......

// Visit the end of the class.
classVisitor.visitEnd();
}
判断是否满足@Component条件
MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
if (isCandidateComponent(metadataReader)) {
	ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
	sbd.setSource(resource);
	if (isCandidateComponent(sbd)) {
		candidates.add(sbd);
	}
}

不在excludeFilters,并且在includeFilters中

protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
	for (TypeFilter tf : this.excludeFilters) {
		if (tf.match(metadataReader, getMetadataReaderFactory())) {
			return false;
		}
	}
	for (TypeFilter tf : this.includeFilters) {
		if (tf.match(metadataReader, getMetadataReaderFactory())) {
			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 = beanDefinition.getMetadata();
	return (metadata.isIndependent() && (metadata.isConcrete() ||
			(metadata.isAbstract() && metadata.hasAnnotatedMethods(Lookup.class.getName()))));
}
@Inherite

https://www.jianshu.com/p/7f54e7250be3

类继承关系中@Inherited的作用

类继承关系中,子类会继承父类使用的注解中被@Inherited修饰的注解

接口继承关系中@Inherited的作用

接口继承关系中,子接口不会继承父接口中的任何注解,不管父接口中使用的注解有没有被@Inherited修饰

类实现接口关系中@Inherited的作用

类实现接口时不会继承任何接口中定义的注解

TODO

一种是编译期直接的扫描,一种是运行期反射

posted @ 2020-11-17 10:59  曹自标  阅读(211)  评论(0编辑  收藏  举报