Java注解浅析一二

Java注解

注解的作用很神奇,平时@RequestMapping用得6,但是我们还是需要了解其本质和原理。

1. 内置注解

  • 1.1 @Override 重写某个方法
  • 1.2 @Deprecated 标明方法或类过时
  • 1.3 @SuppressWarnings

2. 通过内置注解了解元注解

  • 2.1 @Override
    这个注解可以被用来修饰方法,并且它只在编译时有效.
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
  • 2.2 @Deprecated
    这个注解它会被文档化,能够保留到运行时,能够修饰构造方法、属性、局部变量、方法、包、参数、类型。
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})
public @interface Deprecated {
}
  • 2.3 @SuppressWarnings
    它能够修饰的程序元素包括类型、属性、方法、参数、构造器、局部变量,只能存活在源码时,取值为String[]。
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
    String[] value();
}

3. 元注解

元注解是注解的注解。
JDK1.5 提供了4个 @Target,@Retention,@Documented,@Inherited,
JDK 1.8提供了2个 @Repeatable和@Native

3.1 @Target

描述注解的使用范围(即:被修饰的注解可以用在什么地方)
在定义注解类时使用了@Target 能够更加清晰的知道它能够被用来修饰哪些对象,它的取值范围定义在ElementType 枚举中。
@Target注解可以用于修饰 packages、types(类、接口、枚举、注解类)、类成员(方法、构造方法、成员变量、枚举值)、方法参数和本地变量(如循环变量、catch参数)

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
    ElementType[] value();
}

ElementType 是枚举类,指定位置包括:

public enum ElementType {
    /** Class, interface (including annotation type), or enum declaration */
    TYPE,
    FIELD,
    METHOD,
    PARAMETER,
    CONSTRUCTOR,
    LOCAL_VARIABLE,
    ANNOTATION_TYPE,
    PACKAGE,
    TYPE_PARAMETER,
    TYPE_USE
}

3.2 @Retention

描述注解保留的时间范围(即:被描述的注解在它所修饰的类中可以被保留到何时) 。

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
    /**
     * Returns the retention policy.
     * @return the retention policy
     */
    RetentionPolicy value();
}

RetentionPolicy 枚举定义了保留策略。

public enum RetentionPolicy {
    SOURCE,
    CLASS,
    RUNTIME
}

做一个实验


public class RetentionTest {
    @souceAnnotation
    public void m1(){

    }
    @classAnnotation
    public void m2(){

    }
    @runtimeAnnotation
    public void m3(){

    }

}

@Retention(RetentionPolicy.SOURCE)
@interface souceAnnotation{

}

@Retention(RetentionPolicy.CLASS)
@interface classAnnotation{

}

@Retention(RetentionPolicy.RUNTIME)
@interface runtimeAnnotation{

}

使用javap -verbose RetentionTest查看字节码。发现@souceAnnotation注解在字节码不存在;@classAnnotation@runtimeAnnotation在字节码存在,状态分别是RuntimeInvisibleAnnotations,RuntimeVisibleAnnotations
在这里插入图片描述

3.3 @Documented

描述在使用 javadoc 工具为类生成帮助文档时是否要保留其注解信息。

3.4 @Inherited

被它修饰的Annotation将具有继承性。如果某个类使用了被@Inherited修饰的Annotation,则其子类将自动具有该注解。

3.5 @Repeatable (Java8)

允许在同一申明类型(类,属性,或方法)的多次使用同一个注解

3.6 @Native (Java8)

使用 @Native 注解修饰成员变量,则表示这个变量可以被本地代码引用,常常被代码生成工具使用。对于 @Native 注解不常使用.

4.通过发射获取注解内容

4.1 关键方法

AnnotatedElement 接口是所有程序元素(Class、Method和Constructor)的父接口,所以程序通过反射获取了某个类的AnnotatedElement对象之后,程序就可以调用该对象的方法来访问Annotation信息。关键方法如下:

//判断该程序元素上是否包含指定类型的注解,存在则返回true,否则返回false
boolean isAnnotationPresent(Class<?extends Annotation> annotationClass)

//返回该程序元素上存在的、指定类型的注解,如果该类型注解不存在,则返回null。
<T extends Annotation> T getAnnotation(Class<T> annotationClass)

//返回该程序元素上存在的所有注解
Annotation[] getAnnotations()

//返回该程序元素上存在的、指定类型的注解数组。
<T extends Annotation> T[] getAnnotationsByType(Class<T> annotationClass)

//返回直接存在于此元素上的所有注解
<T extends Annotation> T getDeclaredAnnotation(Class<T> annotationClass)

//返回直接存在于此元素上的所有注解。
<T extends Annotation> T[] getDeclaredAnnotationsByType(Class<T> annotationClass)

//返回直接存在于此元素上的所有注解及注解对应的重复注解容器。
Annotation[] getDeclaredAnnotations()

4.2 获取注解信息

创建自定义注解SimpleAnnotation

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SimpleAnnotation {
    public String title() default "";
    public String description() default "";
}

在Main类中标记注解

public class Main {

    @Override
    @SimpleAnnotation(title = "toString",description = "override toString")
    public String toString() {
        return "Main{}";
    }

    @Deprecated
    @SimpleAnnotation(title = "oldmethod",description = "do not use it")
    public static void old(){
        System.out.println("old method call ");
    }

    @SuppressWarnings({"unchecked","desc"})
    @SimpleAnnotation(title = "raw method",description = "suppress warning")
    public static void raw(){
        List l1 = new ArrayList<>();
        l1.add("ab");
        old();
    }

    public static void main(String[] args) {
        Method[] methods = Main.class.getMethods();
        for(Method method:methods) {
            if(method.isAnnotationPresent(SimpleAnnotation.class)){
                for(Annotation annotation:method.getDeclaredAnnotations()) {
                    System.out.println("method "+ method.getName() +"() has an annotation : "+ annotation.annotationType()+",title="+method.getAnnotation(SimpleAnnotation.class).title());
                }
            }
        }
    }
}

运行结果:

method toString() has an annotation : interface com.annotation.reflection.SimpleAnnotation,title=toString
method old() has an annotation : interface java.lang.Deprecated,title=oldmethod
method old() has an annotation : interface com.annotation.reflection.SimpleAnnotation,title=oldmethod
method raw() has an annotation : interface com.annotation.reflection.SimpleAnnotation,title=raw method

5 自定义注解

5.1 创建自定义注解类

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

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

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

5.2 创建测试类

public class TestCase01 {
	@MyBefore
	public void init() {
		System.out.println("初始化...");
	}

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

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

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

5.3 创建框架实现注解类

public class MyJunitFrameWork {
	public static void main(String[] args) throws Exception {
		// 1.先找到测试类的字节码:Test
		Class clazz = TestCase01.class;
		Object obj = clazz.newInstance();
		// 2.获取EmployeeDAOTest类中所有的公共方法
		Method[] methods = clazz.getMethods();
		/* 3.迭代出每一个Method对象 判断哪些方法上使用了@MyBefore/@MyAfter/@MyTest注解
                */
		List<Method> mybeforeList = new ArrayList<>();
		List<Method> myAfterList = new ArrayList<>();
		List<Method> myTestList = new ArrayList<>();
		for (Method method : methods) {
			if(method.isAnnotationPresent(MyBefore.class)){
				//存储使用了@MyBefore注解的方法对象
				mybeforeList.add(method);
			}else if(method.isAnnotationPresent(MyTest.class)){
				//存储使用了@MyTest注解的方法对象
				myTestList.add(method);
			}else if(method.isAnnotationPresent(MyAfter.class)){
				//存储使用了@MyAfter注解的方法对象
				myAfterList.add(method);
			}
		}

		// 执行方法测试
		for (Method testMethod : myTestList) {
			// 先执行@MyBefore的方法
			for (Method beforeMethod : mybeforeList) {
				beforeMethod.invoke(obj);
			}
			// 测试方法
			testMethod.invoke(obj);
			// 最后执行@MyAfter的方法
			for (Method afterMethod : myAfterList) {
				afterMethod.invoke(obj);
			}
		}
	}
}

5.4 运行结果

初始化...
save...
销毁...
初始化...
delete...
销毁...

总结

注解是JDK1.5版本开始引入的一个特性,用于对代码进行说明,可以对包、类、接口、字段、方法参数、局部变量等进行注解。它是框架学习和设计者必须掌握的基础。

java基础 系列在github上有一个开源项目,主要是本系列博客的demo代码。https://github.com/forestnlp/javabasic
如果您对软件开发、机器学习、深度学习有兴趣请关注本博客,将持续推出Java、软件架构、深度学习相关专栏。
您的支持是对我最大的鼓励。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

悟空学编程

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值