因为在 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 通过注解类,可以编写测试用例时,添加一系列的注解,可以在测试结果中生成这些信息,从而在测试报告中展示它们
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());
}