带你学习自定义注解
1.1注解
自定义注解主要是在jdk1.5之后提供的一种语法,其主要作用是编译检查(比如@override)和代码分析(通过代码中添加注解,利用注解解析器对添加了注解的代码进行分析,获取想要的结果,一般自定义的注解都是这一种功能)。
1.2常用的jdk的注解
JDK提供的注解最常用的是3个,@Override,@Deprecated和@SuppressWarnings.
@Override:最常见的注解之一了,表示子类重写了父类的方法,或者实现了接口的方法
@Deprecated:表示是已经过期,建议换个方法,使用的话该方法会被编辑器画一条横线,表示已经弃用。
@SuppressWarnings:表示抑制警告的意思。比如我们新建一个变量,但是没有用他,编辑器会提示,该变量没有使用,但是如果添加@SuppressWarnings相关注解就不会提示了
2.0自定义注解
除了使用java自带的注解以外,我们也可以自己定义注解来使用,代码过程中,注解是一个很方便的东西,比如:权限控制,日志记录等等。
2.0自定义注解的基本语法
1.定义一个注解和定义接口类似,只不过是在interface前边加@符号,在它的内部可以添加属性值
2.修饰符 返回值类型 属性名() [default value]
3.其中,修饰符只能使用public和abstract,返回值类型使用基本类型、数组、枚举,字符串、注解都可以使用,但是注意,数组要是一维数组
4.要定义自定义注解,还要使用元注解,用于修饰自定义注解,一般的话会使用三种
@Retention、@Target、@Documented
(下边给大家解释这三个分别是什么意思)
2.1@Retention注解
@Retention:表示的是这个注解的生命周期,其中有三个枚举变量可以选择
1.RetentionPolicy.SOURCE:表示的是该类在编译为字节码时便弃掉
2.RetentionPolicy.CLASS:表示为该注解被编译为字节码之后被虚拟机加载完后弃掉
3.RetentionPolicy.RUNTIME:表示该注解的可见范围是多少,这里给的是RUNTIME运行时都可见,大部分时候使用的也都是RUNTIME,因为RUNTIME可以通过反射得到该注解的信息。
public enum RetentionPolicy {
/**
* Annotations are to be discarded by the compiler.
* SOURCE级别表示代码级别可见,经过编译器编译生成字节码对象时,此注解就没了。
* 比如@override就是代码级别可见
*/
SOURCE,
/**
* Annotations are to be recorded in the class file by the compiler
* but need not be retained by the VM at run time. This is the default
* behavior.
* CLASS表示字节码对象级别可见,但是字节码对象被虚拟机加载时,
* 这个注解会被抛弃,这是默认的可见级别
*/
CLASS,
/**
* Annotations are to be recorded in the class file by the compiler and
* retained by the VM at run time, so they may be read reflectively.
*
* @see java.lang.reflect.AnnotatedElement
* RUNTIME表示运行时也可见,当虚拟机加载字节码对象时,此注解仍然可见。
* 因此可以通过反射获取注解信息,然后完成相应的注解解析工作,一般自定义的注解都是运行时可见。
*/
RUNTIME
}
2.2@Target注解
@Target:用于修饰此注解可以用于什么类型上。比如注解可以用在类级别、方法、成员字段或者构造函数上。那么它自然也有枚举变量可以选择。
1.METHOD:使用在方法上
2.FIELD:使用在变量上
3.TYPE:使用在类上
4.CONSTRUCTOR:使用在构造函数上
5.PACKAGE:使用在包上
6.PARAMETER:使用在方法参数上
7.LOCAL_VARIABLE:使用在局部变量上
8.ANNOTATION_TYPE:使用在注解上的注解
public enum ElementType {
/** Class, interface (including annotation type), or enum declaration 可以修饰类*/
TYPE,
/** Field declaration (includes enum constants) 可以修饰字段*/
FIELD,
/** Method declaration 可以修饰方法*/
METHOD,
/** Formal parameter declaration */
PARAMETER,
/** Constructor declaration 构造方法*/
CONSTRUCTOR,
/** Local variable declaration */
LOCAL_VARIABLE,
/** Annotation type declaration */
ANNOTATION_TYPE,
/** Package declaration */
PACKAGE,
/**
* Type parameter declaration
*
* @since 1.8
*/
TYPE_PARAMETER,
/**
* Use of a type
*
* @since 1.8
*/
TYPE_USE
}
2.3@Documented注解
@Documented:表示该注解可以写入javadoc中
3.0下边上代码
上面这个注解定义说明此注解可以用在类,方法和成员地段上。是运行时可见的,其内有一个属性,默认值是“苹果”。
import java.lang.annotation.*;
/**
* Target:(表示该注解可以使用在方法,类,变量上)
* 1.METHOD:使用在方法上
* 2.FIELD:使用在变量上
* 3.TYPE:使用在类上
* 4.CONSTRUCTOR:使用在构造函数上
* 5.PACKAGE:使用在包上
* 6.PARAMETER:使用在方法参数上
* 7.LOCAL_VARIABLE:使用在局部变量上
* 8.ANNOTATION_TYPE:使用在注解上的注解
* (其实多用用就会发现注解特别方便快捷~~~~多练习吧)
* Documented:表示该注解可以写入javadoc中
* Retention:
* 1.RUNTIME:表示该注解的可见范围是多少,这里给的是RUNTIME运行时都可见,大部分时候使用的也都是RUNTIME,因为RUNTIME可以通过反射得到该注解的信息
* 2.SOURCE:表示的是该类在编译为字节码时便弃掉
* 3.CLASS:表示为该注解被编译为字节码之后被虚拟机加载完后弃掉
*/
@Target({ElementType.METHOD,ElementType.FIELD,ElementType.TYPE})
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface FruitName {
public String value() default "苹果";
}
然后我们定义一种叫苹果的水果。其内部有个成员变量appleName。我们在这个成员字段上加上@FruitName注解。
package me.gacl.annotation.entity;
import me.gacl.annotation.anno.FruitName;
public class Apple {
@FruitName(value = "草莓")
private String fruitName;
}
3.1关于自定义注解的解析
上面提到,自定义的注解需要定义注解的可见范围。一般我们都定义为运行时可见。因此,通过反射,我们可以拿到注解的内容。通过反射拿到代码的注解内容,进行相关的逻辑处理工作,以达到注解的目的。
通过反射获得注解内容的常用方法有
T getAnnotation(Class) : 获得当前对象的指定的注解。
Annotation[] getAnnotations() X: 获得当前对象的所有注解
boolean isAnnotationPresent(annotationClass): 当前对象是否有注解
getDeclaredMethods():获取这个类中的所有方法
getDeclaredFields():获取这个类中所有的属性(包括私有属性)
getFeilds():获取这个类中所有的属性(不包括私有属性)
package me.gacl.annotation.common;
import me.gacl.annotation.anno.FruitName;
import me.gacl.annotation.entity.Apple;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class FruitUtil {
public static void main(String[] args) {
FruitUtil.getInfomation(Apple.class);
}
/**
* 该方法通过java的反射机制获取类中的注解信息,
* @param tClass Class<?>作为参数表示可以传入任意对象
*/
public static void getInfomation(Class<?> tClass){
//获取到该类中的所有属性,其实也可以通过getFields()获取属性,但是通过getFeilds获取不到类中private修饰的属性信息
Field[] fields=tClass.getDeclaredFields();
//这个便是获取该类中的所有方法
Method[] methods=tClass.getDeclaredMethods();
for (Field field :fields) {
//判断如果该字段含有FruitName注解的信息
if(field.isAnnotationPresent(FruitName.class)){
//获取该字段上注解的信息
FruitName fruitName=field.getAnnotation(FruitName.class);
//输出该注解的的信息
System.out.println(fruitName.value());
}
}
}
}
最后运行结果为:
"C:\Program Files\Java\jdk1.8.0_141\bin\java.exe" "-javaagent:C:\Installation\IntelliJ IDEA 2018.1.6\lib\idea_rt.jar=65037:C:\Installation\IntelliJ IDEA 2018.1.6\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.8.0_141\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_141\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_141\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_141\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_141\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_141\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_141\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_141\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_141\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_141\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_141\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_141\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_141\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_141\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_141\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_141\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_141\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_141\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_141\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_141\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_141\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_141\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_141\jre\lib\rt.jar;C:\personalExpansion\Annotation\target\classes;C:\work\repository\org\springframework\boot\spring-boot-starter\2.1.0.RELEASE\spring-boot-starter-2.1.0.RELEASE.jar;C:\work\repository\org\springframework\boot\spring-boot\2.1.0.RELEASE\spring-boot-2.1.0.RELEASE.jar;C:\work\repository\org\springframework\spring-context\5.1.2.RELEASE\spring-context-5.1.2.RELEASE.jar;C:\work\repository\org\springframework\spring-aop\5.1.2.RELEASE\spring-aop-5.1.2.RELEASE.jar;C:\work\repository\org\springframework\spring-beans\5.1.2.RELEASE\spring-beans-5.1.2.RELEASE.jar;C:\work\repository\org\springframework\spring-expression\5.1.2.RELEASE\spring-expression-5.1.2.RELEASE.jar;C:\work\repository\org\springframework\boot\spring-boot-autoconfigure\2.1.0.RELEASE\spring-boot-autoconfigure-2.1.0.RELEASE.jar;C:\work\repository\org\springframework\boot\spring-boot-starter-logging\2.1.0.RELEASE\spring-boot-starter-logging-2.1.0.RELEASE.jar;C:\work\repository\ch\qos\logback\logback-classic\1.2.3\logback-classic-1.2.3.jar;C:\work\repository\ch\qos\logback\logback-core\1.2.3\logback-core-1.2.3.jar;C:\work\repository\org\apache\logging\log4j\log4j-to-slf4j\2.11.1\log4j-to-slf4j-2.11.1.jar;C:\work\repository\org\apache\logging\log4j\log4j-api\2.11.1\log4j-api-2.11.1.jar;C:\work\repository\org\slf4j\jul-to-slf4j\1.7.25\jul-to-slf4j-1.7.25.jar;C:\work\repository\javax\annotation\javax.annotation-api\1.3.2\javax.annotation-api-1.3.2.jar;C:\work\repository\org\springframework\spring-core\5.1.2.RELEASE\spring-core-5.1.2.RELEASE.jar;C:\work\repository\org\springframework\spring-jcl\5.1.2.RELEASE\spring-jcl-5.1.2.RELEASE.jar;C:\work\repository\org\yaml\snakeyaml\1.23\snakeyaml-1.23.jar;C:\work\repository\org\slf4j\slf4j-api\1.7.25\slf4j-api-1.7.25.jar" me.gacl.annotation.common.FruitUtil
草莓
Process finished with exit code 0