注解
注解是那些插入到源代码中使用其他工具可以对其进行处理的标签,其不会改变程序的编译方式,只是对源程序的解读,也就是说源代码正常执行,我们会在特殊的地方对注解进行解读。两部分是分开进行的。
1、定义注解
//定义TestMyAnnotation 注解
public @interface TestMyAnnotation {
}
1.1、元注解
元注解其实就是注解的注解,用来解释你所定义的注解怎么用,用在什么地方,下面就来详细解释元注解。
元注解:@Retention、@Documented、@Target、@Inherited、@Repeatable(jdk8新加入的)共5种。
@Retention: 当这个元注解应用在一个自定义注解时,用来指明自定义注解可以保留多久。
它的取值如下:
- RetentionPolicy.SOURCE 不包括在类文件中的注解(用来做源代码层级的提示作用)。
- RetentionPolicy.CLASS(默认值) 包括在类文件中的注解,但是虚拟机不需要将他们载入(用来做编译的校验)。
- RetentionPolicy.RUNTIME 包括在类文件中的注解,并由虚拟机载入,可以通过反射来得到它们(springMVC等框架常用)。
//定义TestMyAnnotation 注解
@Retention(RetentionPolicy.RUNTIME)
public @interface TestMyAnnotation {
}
上述自定义注解可以载入虚拟机并通过反射得到内容,后续我们会讲如何操作。
@Documented:当这个元注解应用在一个自定义注解时,该自定义注解会归档,用这个注解注释的方法等,在用javadoc归档时,会随着方法一起进行归档。
@Target:当这个元注解应用在一个自定义注解时,用来指明自定义注解可以可以作用的地方。
- ElementType.ANNOTATION_TYPE 可以给一个注解进行注解
- ElementType.CONSTRUCTOR 可以给构造方法进行注解
- ElementType.FIELD 可以给属性进行注解
- ElementType.LOCAL_VARIABLE 可以给局部变量进行注解
- ElementType.METHOD 可以给方法进行注解
- ElementType.PACKAGE 可以给一个包进行注解
- ElementType.PARAMETER 可以给一个方法内的参数进行注解
- ElementType.TYPE 可以给一个类型进行注解,比如类、接口、枚举。
@Inherited:当这个元注解应用在一个自定义注解时,该自定义注解注解了一个父类,如果它的子类没有应用任何注解,那么它的子类及孙类也拥有这一注解。
//定义TestMyAnnotation 注解
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface TestMyAnnotation {
}
@TestMyAnnotation
class A {}
//子类未应用任何注解,自动应用@TestMyAnnotation注解
class B extends A{}
@Repeatable:当这个元注解应用在一个自定义注解时,该自定义注解可重复使用。例如一个人既是老师又是父亲。
@interface Persons {
Person [] value();
}
@Repeatable(Persons.class)
@interface Person{
String role() default "";
}
@Person(role="teacher")
@Person(role="father")
class Man{
}
注意:@Repeatable里面需要一个容器,它里面需要存放其他的注解,该容器注解里面必须有一个属性 value且类型为被@Repeatable注解过的注解数组。
1.2、注解属性
注解的属性也叫做成员变量。注解只有成员变量,没有方法。注解的成员变量在注解的定义中以“无形参的方法”形式来声明,其方法名定义了该成员变量的名字,其返回值定义了该成员变量的类型。
注解属性的类型有以下几种:
- 基本类型(int、short、long、byte、char、double、float、boolean)
- String类型
- Class类型
- enum(枚举)类型
- 注解类型(@interface)
- 由前面所述类型组成的数组类型。
一个简单的例子:
@interface Persons {
Person [] value();
}
@interface Person{
enum Status {BUSY,FREE}
String role() default "";
int age() default 10;
String[] duty() default {"teach","learn","work"};
Class<?> test() default Void.class;
TestMyAnnotation ref() default @TestMyAnnotation;
Status status() default Status.BUSY;
}
//未写明的属性使用默认属性
@Person()
@Person(role="father",age=20,duty={"raise"})
class Man{
}
若注解中仅仅只有value这一个属性时,应用这个注解时可以直接将属性值填入括号中
@interface Person{
int value() default 0;
}
//直接写入属性值 即为value属性的值
@Person(10)
class Man{
}
若注解中没有任何属性或属性均有默认值时,则应用注解时,不必使用括号
@interface Test{
}
@Test
class Man{
}
2、jdk自带注解
注解 | 应用场合 | 目的 |
---|---|---|
@Deprecated | 全部 | 将项标记为过时的 |
@SuppressWarnings | 除了包和注解之外的所有情况 | 阻止某个给定类型的警告消息 |
@SafeVarargs | 方法和构造器 | 断言varargs参数可安全使用 |
@Override | 方法 | 检查该方法是否覆盖了一个超类方法 |
@FunctionalInterface | 接口 | 将接口标记为只有一个抽象方法的函数式接口 |
@PostConstruct | 方法 | 被标记的方法应该在构造之后被调用 |
@PreDestroy | 方法 | 被标记的方法应该在对象被移除之前调用 |
@Resource | 类、接口、方法、域 | 在类或接口上:标记为其他地方要用到的资源。 在方法或域上:为“注入”而标记 |
@Resources | 类、接口 | 一个资源数组 |
@Generated | 全部 | 供代码生成工具来使用 |
@Deprecated:该注解添加到任何不在鼓励的使用项上。会产生横线的效果。
@Override:这个注解应该会常用,当覆盖父类的方法时,方法前会有此注释。
public class Test {
@Override
protected Object clone() throws CloneNotSupportedException {
// TODO Auto-generated method stub
return super.clone();
}
}
@PostConstruct:被标记的方法应该在构造之后被调用
@PreDestroy:被标记的方法应该在对象被移除之前调用
这两个注解是一种提议,如果你不遵守也不会报错,但在javaEE5 Servlet 和 spring框架中这两个注解声明的方法被强制调用。
3、利用反射获得注解内容
package com.tjy.learn;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
@Target(ElementType.TYPE) //该注解可以用在接口、类、枚举
@Retention(RetentionPolicy.RUNTIME) //保留到运行期间
@interface TestClass {
String value() default "com.tjy.learn.Test";
}
@Target(ElementType.METHOD) //该注解可以用在方法
@Retention(RetentionPolicy.RUNTIME) //保留到运行期间
@interface TestMethod{
//方法名
String name() default "";
//返回类型
Class<?> type() default Void.class;
//参数个数
int args() default 0;
}
@Target(ElementType.FIELD)//该注解可以用在属性
@Retention(RetentionPolicy.RUNTIME) //保留到运行期间
@interface TestFiled{
String name() default "";
}
@TestClass("com.tjy.learn.myClass")
class myClass{
@TestFiled(name="id")
int id;
@TestFiled(name="name")
String name;
@TestFiled
String value;
@TestFiled
Test test;
@TestMethod(name="getId",type=Integer.class,args=0)
public int getId() {
return id;
}
@TestMethod
public void setId(int id) {
this.id = id;
}
@TestMethod(name="getName",type=String.class,args=0)
public String getName() {
return name;
}
@TestMethod
public void setName(String name) {
this.name = name;
}
@TestMethod(name="getValue",type=String.class,args=0)
public String getValue() {
return value;
}
@TestMethod(name="setValue",type=Void.class,args=1)
public void setValue(String value) {
this.value = value;
}
@TestMethod
public Test getTest() {
return test;
}
@TestMethod(name="setTest",type=Void.class,args=1)
public void setTest(Test test) {
this.test = test;
}
}
public class TestMyAnnotation {
public static void main(String[] args) throws ClassNotFoundException {
//获得bean的全路径名称
String className = "com.tjy.learn.myClass";
//通过反射获得类
Class clazz = Class.forName(className);
//判断该类上是否有注解@TestClass
boolean hasAnnotation = clazz.isAnnotationPresent(TestClass.class);
if(hasAnnotation){
TestClass testClass = (TestClass) clazz.getAnnotation(TestClass.class);
System.out.println("进行类:"+className+"分析,类路径:"+testClass.value());
}
//获取类的成员变量
Field [] fields = clazz.getDeclaredFields();
for(Field f:fields){
if(f.isAnnotationPresent(TestFiled.class)){
//设置属性--反射可访问
f.setAccessible(true);
TestFiled testFiled = f.getAnnotation(TestFiled.class);
System.out.println("进行属性:"+f.getName()+"分析,该属性名字为:"+testFiled.name());
}
}
//获取类的成员变量
Method [] Methods = clazz.getMethods();
for(Method m:Methods){
if(m.isAnnotationPresent(TestMethod.class)){
TestMethod testMethod = m.getAnnotation(TestMethod.class);
System.out.println("进行方法:"+m.getName()+"分析,该属性名字为:"+testMethod.name()+" 返回类型为"+testMethod.type().getName()+" 参数个数:"+testMethod.args());
}
}
}
}
输出结果:
进行类:com.tjy.learn.myClass分析,类路径:com.tjy.learn.myClass
进行属性:id分析,该属性名字为:id
进行属性:name分析,该属性名字为:name
进行属性:value分析,该属性名字为:
进行属性:test分析,该属性名字为:
进行方法:getName分析,该属性名字为:getName 返回类型为java.lang.String 参数个数:0
进行方法:getValue分析,该属性名字为:getValue 返回类型为java.lang.String 参数个数:0
进行方法:getId分析,该属性名字为:getId 返回类型为java.lang.Integer 参数个数:0
进行方法:setName分析,该属性名字为: 返回类型为java.lang.Void 参数个数:0
进行方法:setValue分析,该属性名字为:setValue 返回类型为java.lang.Void 参数个数:1
进行方法:setId分析,该属性名字为: 返回类型为java.lang.Void 参数个数:0
进行方法:getTest分析,该属性名字为: 返回类型为java.lang.Void 参数个数:0
进行方法:setTest分析,该属性名字为:setTest 返回类型为java.lang.Void 参数个数:1