Java的注解

0x00 注解例子

写一个注解

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

@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    String getValue() default "no description";
}

使用一个注解

@MyAnnotation(getValue = "annotation on class")
public class User {
    @MyAnnotation(getValue = "annotation on field")
    public String name;

    @MyAnnotation(getValue = "annotation on method")
    public void hello() {}
}

获取一个注解

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

public class Test {
    public static void main(String[] args) throws Exception {
        Class<User> clazz = User.class;
        MyAnnotation annotationOnClass = clazz.getAnnotation(MyAnnotation.class);
        System.out.println(annotationOnClass.getValue());

        Field name = clazz.getField("name");
        MyAnnotation annotationOnField = name.getAnnotation(MyAnnotation.class);
        System.out.println(annotationOnField.getValue());

        Method hello = clazz.getMethod("hello", null);
        MyAnnotation annotationOnMethod = hello.getAnnotation(MyAnnotation.class);
        System.out.println(annotationOnMethod.getValue());
    }
}

说明

一般像我这样的Python程序员,看到这个注解,想到的肯定是Python里的装饰器,但是实际上,这两者基本没有什么关系

如果对一个方法使用了注解,然后调用方法,会发现注解没有任何装饰器的作用,和我们上面获取一个注解的方式一样,Java里的注解主要是用来被非注解的方法调用,一般是通过反射的方式,也就是在调用方法前,获取它的注解并操作注解的内容

Java里的注解就像标签,是程序判断执行的依据,比如@Before就是在测试方法之前执行

@Retention(RetentionPolicy.RUNTIME)是元注解,就是加在注解上的注解,Rentention用来指定注解的保留策略

因为注解主要被反射读取,反射执行读取内存中的字节码信息,保留策略设置为RUNTIME,可以运行时读取,如果不设置,会报错,不信可以去掉@Retention(RententionPolicy.RUNTIME)尝试一下

大多数情况下,我们只需要使用注解,无需定义和执行,框架会将注解类和读取注解的程序隐藏起来,不阅读源码不知道注解怎么使用

0x01 模拟框架的注解

写几个注解

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MyBefore {
}

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MyTest {
}

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MyAfter {
}

使用注解

public class User {
    @MyBefore
    public void init() {
        System.out.println("init");
    }

    @MyAfter
    public void destroy() {
        System.out.println("destroy");
    }

    @MyTest
    public void testSave() {
        System.out.println("save");
    }

    @MyTest
    public void testDelete() {
        System.out.println("delete");
    }
}

框架如何调用注解

public class Test {
    public static void main(String[] args) throws Exception {
        Class clazz = User.class;
        Object obj = clazz.newInstance();
        Method[] methods = clazz.getMethods();

        List<Method> myBeforeList = new ArrayList<Method>();
        List<Method> myAfterList = new ArrayList<Method>();
        List<Method> myTestList = new ArrayList<Method>();

        for (Method method : methods) {
            if (method.isAnnotationPresent(MyBefore.class)) {
                myBeforeList.add(method);
            } else if (method.isAnnotationPresent(MyTest.class)) {
                myTestList.add(method);
            } else if (method.isAnnotationPresent(MyAfter.class)) {
                myAfterList.add(method);
            }
        }

        for (Method testMethod : myTestList) {
            for (Method beforeMethod : myBeforeList) {
                beforeMethod.invoke(obj);
            }
            testMethod.invoke(obj);
            for (Method afterMethod : myAfterList) {
                afterMethod.invoke(obj);
            }
        }
    }
}

执行结果

init
save
destroy
init
delete
destroy

自己写一遍体会一下就能明白

通过反射获取一个类,获取类中的方法和属性,根据方法和属性的注解和注解的参数进行一些判断处理操作

坚持原创技术分享,您的支持将鼓励我继续创作!