文章目录
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、软件架构、深度学习相关专栏。
您的支持是对我最大的鼓励。