Method getAnnotations顺序

2018-11-20  本文已影响0人  jeho0815

问题背景

在使用ServiceComb开发的进程启动时,生成契约偶现不一致(描述偶尔为空,导致赋值了一个默认的描述),导致服务注册不上。开发定位过程中,最后发现是因为getAnnotations这个方法返回的顺序order不一致。所以只能看下源码为啥会出现这种情况。

源码分析

从Method方法跟踪getAnnotations,最终会调用Executable.getDeclaredAnnotations

    /**
     * {@inheritDoc}
     */
    public Annotation[] getDeclaredAnnotations()  {
        return AnnotationParser.toArray(declaredAnnotations());
    }

    private transient Map<Class<? extends Annotation>, Annotation> declaredAnnotations;

    private synchronized  Map<Class<? extends Annotation>, Annotation> declaredAnnotations() {
        if (declaredAnnotations == null) {
            Executable root = getRoot();
            if (root != null) {
                declaredAnnotations = root.declaredAnnotations();
            } else {
                declaredAnnotations = AnnotationParser.parseAnnotations(
                    getAnnotationBytes(),
                    sun.misc.SharedSecrets.getJavaLangAccess().
                    getConstantPool(getDeclaringClass()),
                    getDeclaringClass());
            }
        }
        return declaredAnnotations;
    }

这个是declaredAnnotations是返回一个map,所以需要排查下这个方法返回的AnnotationParser.parseAnnotations 是否有序。
跟踪到AnnotationParser实现里面,这个需要下载openjdk得源码才可以。

private static Map<Class<? extends Annotation>, Annotation> parseAnnotations2(
                byte[] rawAnnotations,
                ConstantPool constPool,
                Class<?> container,
                Class<? extends Annotation>[] selectAnnotationClasses) {
        Map<Class<? extends Annotation>, Annotation> result =
            new LinkedHashMap<Class<? extends Annotation>, Annotation>();
        ByteBuffer buf = ByteBuffer.wrap(rawAnnotations);
        int numAnnotations = buf.getShort() & 0xFFFF;
        for (int i = 0; i < numAnnotations; i++) {
            Annotation a = parseAnnotation2(buf, constPool, container, false, selectAnnotationClasses);
            if (a != null) {
                Class<? extends Annotation> klass = a.annotationType();
                if (AnnotationType.getInstance(klass).retention() == RetentionPolicy.RUNTIME &&
                    result.put(klass, a) != null) {
                        throw new AnnotationFormatError(
                            "Duplicate annotation for class: "+klass+": " + a);
            }
        }
        }
        return result;
    }

rawAnnotations是一个byte数组,是Method.getAnnotationBytes返回的,这个是从class文件加载得到的,编译是什么样的就返回什么样的,map也是一个LinkedHashMap,可以保证顺序。所以正常情况肯定是有序的,所以就排查了代码的问题。

问题原因

最后通过打印getAnnotations数组,发现多了很多其他的注解,就怀疑是不是启动过程中有别的东西对它进行了修改。最终发现是启动过程中设置了一个javaagent,这个agent会织入一些注解,顺序可能会重新排,导致会随机出现一些该问题。

上一篇下一篇

猜你喜欢

热点阅读