zoukankan      html  css  js  c++  java
  • 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

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

    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出。
  • 相关阅读:
    阿里规范
    阿里规范
    阿里规范
    sql 优化步骤
    事务的并发问题:脏读、幻读和不可重复读
    Hive 常见面试题(二)
    yield 的使用
    Java 线程状态
    Lambda 表达式推演全过程
    IDEA 代码自动补全/自动联想 功能
  • 原文地址:https://www.cnblogs.com/caozibiao/p/13993119.html
Copyright © 2011-2022 走看看