缘由

因为在 spring,Allure 等工具中都存在注解的使用,以前都是在用,甚至不解它的概念,补知识坑来了

注解的分类

按运行机制分:源码注解,编译时注解,运行时注解
按注解来源分:jdk 注解,第三方注解,自定义注解

自定义注解

package com.carl.annotationdemo;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME) //编译时注解,括号中为RetentionPolicy.CLASS
@Target({ElementType.FIELD})
@Inherited
@Documented
public @interface UserProp {

    String name() default "";
    int age() default 18;

}

自定义注解的使用

package com.carl.annotationdemo;

public class User {
    @UserProp(name = "xixi")
    private static String name;

    @UserProp(age = 12)
    private static int age;

    public static void main(String[] args) {
        System.out.println(name);
        System.out.println(age);
    }

}

解析注解

通过反射获取类,函数或成员上的运行时注解,从而实现动态控制程序运行的逻辑

package com.carl.annotationdemo;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

import com.carl.annotationdemo.Desc;

public class ParseAnno {

    public static void main(String[] args) {
        /** 通过类加载器加载类 **/
        try {
            Class<?> c = Class.forName("com.carl.annotationdemo.UserBean");
            /** 找类上面的注解 **/
            {
                boolean isExist = c.isAnnotationPresent(Desc.class);
                System.out.println("c.isAnnotationPresent:" + isExist);
                if (isExist) {
                    Annotation[] as = c.getAnnotations();
                    for (Annotation a : as) {
                        if (a instanceof Desc) {
                            System.out.println(((Desc)a).name());
                        }

                    }
                }
            }

            Method[] method = c.getMethods();
            System.out.println("method.length:" + method.length);
            for (Method m : method) {
                boolean isExist = m.isAnnotationPresent(Desc.class);
                if (isExist) {
                    System.out.println("method anno:" + m.getAnnotation(Desc.class).name());
                }
            }

            for (Method m : method) {
                Annotation[] an = m.getAnnotations();
                System.out.print(m.getName() + " --- ");
                System.out.println("an.length:" + an.length);
            }

            Field[] field = c.getFields();
            System.out.println("field.length:" + field.length);
            for (Field f : field) {
                boolean isExist = f.isAnnotationPresent(Desc.class);
                if (isExist) {
                    System.out.println("filed anno:" + f.getAnnotation(Desc.class));
                }
            }

            for (Field f : field) {
                System.out.println("f.getAnnotations().length:" + f.getAnnotations().length);
            }

            Field[] deClaredField = c.getDeclaredFields();
            System.out.println("deClaredField.length:" + deClaredField.length);
                        Field.setAccessible(deClaredField, true);
            for (Field f : deClaredField) {
                //f.setAccessible(true);

                System.out.println("f.getName():" + f.getName());
                boolean isExist = f.isAnnotationPresent(Desc.class);
                if (isExist) {
                    System.out.println("deClaredField anno:" + f.getAnnotation(Desc.class));
                }

                System.out.println("f.getAnnotations():" + f.getAnnotations().length);
                for (Annotation a : f.getAnnotations()) {
                    if (a instanceof Desc) {
                        System.out.println(((Desc) a).name());
                    }
                }
            }

        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

}

Allure

Allure 通过注解类,可以编写测试用例时,添加一系列的注解,可以在测试结果中生成这些信息,从而在测试报告中展示它们

问题

问一

package io.qameta.allure;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 *  Annotation that allows to attach a description for a test or for a step.
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Description {

    /**
     * Simple description text as String.
     *
     * @return Description text.
     */
    String value() default "";

    /**
     * Use annotated method's javadoc to extract description that
     * supports html markdown.
     *
     * @return boolean flag to enable description extraction from javadoc.
     */
    boolean useJavaDoc() default false;
}

这里的 Description 明明是一个运行时注解,但看代码好像也在编译时去做动作呢

/**
 * @author Egor Borisov ehborisov@gmail.com
 */
@SupportedAnnotationTypes("io.qameta.allure.Description")
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class DescriptionsProcessor extends AbstractProcessor {

    private Filer filer;
    private Elements elementUtils;
    private Messager messager;

    @Override
    @SuppressWarnings("PMD.AvoidSynchronizedAtMethodLevel")
    public synchronized void init(final ProcessingEnvironment env) {
        super.init(env);
        filer = env.getFiler();
        elementUtils = env.getElementUtils();
        messager = env.getMessager();
    }

    @Override
    public boolean process(final Set<? extends TypeElement> annotations, final RoundEnvironment env) {
        final Set<? extends Element> elements = env.getElementsAnnotatedWith(Description.class);
        elements.forEach(el -> {
            if (!el.getAnnotation(Description.class).useJavaDoc()) {
                return;
            }
            final String docs = elementUtils.getDocComment(el);
            final List<String> typeParams = ((ExecutableElement) el).getParameters().stream()
                    .map(param -> param.asType().toString()).collect(Collectors.toList());
            final String name = el.getSimpleName().toString();

            final String hash = generateMethodSignatureHash(name, typeParams);
            try {
                final FileObject file = filer.createResource(StandardLocation.CLASS_OUTPUT,
                        "allureDescriptions", hash);
                try (Writer writer = file.openWriter()) {
                    writer.write(docs);
                }
            } catch (IOException e) {
                messager.printMessage(Diagnostic.Kind.WARNING,
                        "Unable to create resource from docs comment of method " + name + typeParams);
            }
        });

        return true;
    }
}

问二

这种 Stream 处理集合的方式也是一脸懵逼啊

@SuppressWarnings("unchecked")
private <T extends Annotation> List<T> getAnnotationsOnClass(final ITestResult result, final Class<T> clazz) {
    return Stream.of(result)
            .map(ITestResult::getTestClass)
            .filter(Objects::nonNull)
            .map(IClass::getRealClass)
            .flatMap(aClass -> Stream.of(aClass.getAnnotationsByType(clazz)))
            .map(clazz::cast)
            .collect(Collectors.toList());
}

参考

  1. 网上某课网视频
  2. https://docs.qameta.io


↙↙↙阅读原文可查看相关链接,并与作者交流