一片文章让你对java注解有一个可以使用的了解 反射获取注解


说明

要深入学习注解,我们就必须能定义自己的注解,并使用注解,在定义自己的注解之前,我们就必须要了解Java为我们提供的元注解和相关定义注解的语法。

内置注解、

元注解:

  元注解的作用就是负责注解其他注解。Java5.0定义了4个标准的meta-annotation类型,它们被用来提供对其它 annotation类型作说明。Java5.0定义的元注解:
    1.@Target,
    2.@Retention,
    3.@Documented,
    4.@Inherited
  这些类型和它们所支持的类在java.lang.annotation包中可以找到。下面我们看一下每个元注解的作用和相应分参数的使用说明。


@Target

 @Target说明了Annotation所修饰的对象范围:Annotation可被用于 packages、types(类、接口、枚举、Annotation类型)、类型成员(方法、构造方法、成员变量、枚举值)、方法参数和本地变量(如循环变量、catch参数)。在Annotation类型的声明中使用了target可更加明晰其修饰的目标。

  作用:用于描述注解的使用范围(即:被描述的注解可以用在什么地方)

  取值(ElementType)有:

    1.CONSTRUCTOR:用于描述构造器
    2.FIELD:用于描述域
    3.LOCAL_VARIABLE:用于描述局部变量
    4.METHOD:用于描述方法
    5.PACKAGE:用于描述包
    6.PARAMETER:用于描述参数
    7.TYPE:用于描述类、接口(包括注解类型) 或enum声明

  使用实例:  

@Target(ElementType.TYPE)
public @interface Table {
    /**
     * 数据表名称注解,默认值为类名称
     * @return
     */
    public String tableName() default "className";
}

@Target(ElementType.FIELD)
public @interface NoDBColumn {

}

注解Table 可以用于注解类、接口(包括注解类型) 或enum声明,而注解NoDBColumn仅可用于注解类的成员变量。


@Retention

@Retention定义了该Annotation被保留的时间长短:某些Annotation仅出现在源代码中,而被编译器丢弃;而另一些却被编译在class文件中;编译在class文件中的Annotation可能会被虚拟机忽略,而另一些在class被装载时将被读取(请注意并不影响class的执行,因为Annotation与class在使用上是被分离的)。使用这个meta-Annotation可以对 Annotation的“生命周期”限制。

作用:表示需要在什么级别保存该注释信息,用于描述注解的生命周期(即:被描述的注解在什么范围内有效)

 取值(RetentionPoicy)有:

    1.SOURCE:在源文件中有效(即源文件保留)
    2.CLASS:在class文件中有效(即class保留)
    3.RUNTIME:在运行时有效(即运行时保留)


 Retention meta-annotation类型有唯一的value作为成员,它的取值来自java.lang.annotation.RetentionPolicy的枚举类型值。具体实例如下:

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Column {
    public String name() default "fieldName";
    public String setFuncName() default "setField";
    public String getFuncName() default "getField"; 
    public boolean defaultDBValue() default false;
}

olumn注解的的RetentionPolicy的属性值是RUTIME,这样注解处理器可以通过反射,获取到该注解的属性值,从而去做一些运行时的逻辑处理

 @Documented

 @Documented用于描述其它类型的annotation应该被作为被标注的程序成员的公共API,因此可以被例如javadoc此类的工具文档化。Documented是一个标记注解,没有成员。

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Column {
    public String name() default "fieldName";
    public String setFuncName() default "setField";
    public String getFuncName() default "getField"; 
    public boolean defaultDBValue() default false;
}

@Inherited

@Inherited 元注解是一个标记注解,@Inherited阐述了某个被标注的类型是被继承的。如果一个使用了@Inherited修饰的annotation类型被用于一个class,则这个annotation将被用于该class的子类。

  注意:@Inherited annotation类型是被标注过的class的子类所继承。类并不从它所实现的接口继承annotation,方法并不从它所重载的方法继承annotation。

  当@Inherited annotation类型标注的annotation的Retention是RetentionPolicy.RUNTIME,则反射API增强了这种继承性。如果我们使用java.lang.reflect去查询一个@Inherited annotation类型的annotation时,反射代码检查将展开工作:检查class和其父类,直到发现指定的annotation类型被发现,或者到达类继承结构的顶层。

@Inherited
public @interface Greeting {
    public enum FontColor{ BULE,RED,GREEN};
    String name();
    FontColor fontColor() default FontColor.GREEN;
}

具体解释

Inherited 是继承的意思,但是它并不是说注解本身可以继承,而是说如果一个超类被 @Inherited 注解过的注解进行注解的话,那么如果它的子类没有被任何注解应用的话,那么这个子类就继承了超类的注解。
说的比较抽象。代码来解释。

@Inherited
@Retention(RetentionPolicy.RUNTIME)
@interface Test {}
@Test
public class A {}
public class B extends A {}

注解 Test 被 @Inherited 修饰,之后类 A 被 Test 注解,类 B 继承 A,类 B 也拥有 Test 这个注解。

可以这样理解:

老子非常有钱,所以人们给他贴了一张标签叫做富豪。

老子的儿子长大后,只要没有和老子断绝父子关系,虽然别人没有给他贴标签,但是他自然也是富豪。

老子的孙子长大了,自然也是富豪。

这就是人们口中戏称的富一代,富二代,富三代。虽然叫法不同,好像好多个标签,但其实事情的本质也就是他们有一张共同的标签,也就是老子身上的那张富豪的标签。

@Repeatable

Repeatable 自然是可重复的意思。@Repeatable 是 Java 1.8 才加进来的,所以算是一个新的特性。

什么样的注解会多次应用呢?通常是注解的值可以同时取多个。

举个例子,一个人他既是程序员又是产品经理,同时他还是个画家。

@interface Persons {
    Person[]  value();
}
@Repeatable(Persons.class)
@interface Person{
    String role default "";
}
@Person(role="artist")
@Person(role="coder")
@Person(role="PM")
public class SuperMan{
}

注意上面的代码,@Repeatable 注解了 Person。而 @Repeatable 后面括号中的类相当于一个容器注解。

什么是容器注解呢?就是用来存放其它注解的地方。它本身也是一个注解。

我们再看看代码中的相关容器注解。

@interface Persons {
    Person[]  value();
}

按照规定,它里面必须要有一个 value 的属性,属性类型是一个被 @Repeatable 注解过的注解数组,注意它是数组。

如果不好理解的话,可以这样理解。Persons 是一张总的标签,上面贴满了 Person 这种同类型但内容不一样的标签。把 Persons 给一个 SuperMan 贴上,相当于同时给他贴了程序员、产品经理、画家的标签。

我们可能对于 @Person(role=”PM”) 括号里面的内容感兴趣,它其实就是给 Person 这个注解的 role 属性赋值为 PM


自定义注解

上边不是方法而是注解的参数


基于以上我们来定义出

  注解参数的可支持数据类型:

     1.所有基本数据类型(int,float,boolean,byte,double,char,long,short)
    2.String类型
    3.Class类型
    4.enum类型
    5.Annotation类型
    6.以上所有类型的数组

Annotation类型里面的参数该怎么设定:
  第一,只能用public或默认(default)这两个访问权修饰.例如,String value();这里把方法设为defaul默认类型;   
  第二,参数成员只能用基本类型byte,short,char,int,long,float,double,boolean八种基本数据类型和 String,Enum,Class,annotations等数据类型,以及这一些类型的数组.例如,String value();这里的参数成员就为String;  
  第三,如果只有一个参数成员,最好把参数名称设为"value",后加小括号.例:下面的例子FruitName注解就只有一个参数成员。


读取注解

这里利用反射去读取注解

 

讲到反射这里我要说一嘴:让大家清楚的明白为什么会有反射这个东东

               在你做ssm项目的时候,你会发现有某些方法中(切这写方法在同一个包中)有 一些公共的字段都是重复的,这时候你想到 用aop切面编程去处理,而这时候你要拦截的是这个包中的所有方法,因为你不可能把这些方法的名字一个一个敲出来去拦截。而就在这时候你想起来了,用注解啊,我只要拦截这个包下的,并且头上有着个我们自定义的注解的方法就好了,那你拦截下来之后发现,

这是一个service层的实现类,大红框是重复的代码,其实是因为controller层传来的categoryDTO内部字段,比DAO需要的category字段少,我们 需要自己去设置,但是我们

通过拦截此类标有我们自定义的注解,并在aop中添加这些缺少的字段不久可以了吗,可是现在程序已经是在运行中了,我们怎么样才能获得这个实体对象呢,这时候就得用到反射来创建了

 那么改如何读取注解呢

我通过用标签来比作注解,前面的内容是讲怎么写注解,然后贴到哪个地方去,而现在我们要做的工作就是检阅这些标签内容。 形象的比喻就是你把这些注解标签在合适的时候撕下来,然后检阅上面的内容信息。


注解与反射

注解通过反射获取。首先可以通过 Class 对象的 isAnnotationPresent() 方法判断它是否应用了某个注解

public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {}

然后通过 getAnnotation() 方法来获取 Annotation 对象。

 public <A extends Annotation> A getAnnotation(Class<A> annotationClass) {}

或者是 getAnnotations() 方法。

public Annotation[] getAnnotations() {}

前一种方法返回指定类型的注解,后一种方法返回注解到这个元素上的所有注解。

如果获取到的 Annotation 如果不为 null,则就可以调用它们的属性方法了。比如

@TestAnnotation()
public class Test {
    public static void main(String[] args) {
        boolean hasAnnotation = Test.class.isAnnotationPresent(TestAnnotation.class);
        if ( hasAnnotation ) {
            TestAnnotation testAnnotation = Test.class.getAnnotation(TestAnnotation.class);
            System.out.println("id:"+testAnnotation.id());
            System.out.println("msg:"+testAnnotation.msg());
        }
    }
}

这个正是 TestAnnotation 中 id 和 msg 的默认值。 

上面的例子中,只是检阅出了注解在类上的注解,其实属性、方法上的注解照样是可以的。同样还是要假手于反射。

@TestAnnotation(msg="hello")
public class Test {
    @Check(value="hi")
    int a;
    @Perform
    public void testMethod(){}
    @SuppressWarnings("deprecation")
    public void test1(){
        Hero hero = new Hero();
        hero.say();
        hero.speak();
    }
    public static void main(String[] args) {
        boolean hasAnnotation = Test.class.isAnnotationPresent(TestAnnotation.class);
        if ( hasAnnotation ) {
            TestAnnotation testAnnotation = Test.class.getAnnotation(TestAnnotation.class);
            //获取类的注解
            System.out.println("id:"+testAnnotation.id());
            System.out.println("msg:"+testAnnotation.msg());
        }
        try {
            Field a = Test.class.getDeclaredField("a");
            a.setAccessible(true);
            //获取一个成员变量上的注解
            Check check = a.getAnnotation(Check.class);
            if ( check != null ) {
                System.out.println("check value:"+check.value());
            }
            Method testMethod = Test.class.getDeclaredMethod("testMethod");
            if ( testMethod != null ) {
                // 获取方法中的注解
                Annotation[] ans = testMethod.getAnnotations();
                for( int i = 0;i < ans.length;i++) {
                    System.out.println("method testMethod annotation:"+ans[i].annotationType().getSimpleName());
                }
            }
        } catch (NoSuchFieldException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            System.out.println(e.getMessage());
        } catch (SecurityException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            System.out.println(e.getMessage());
        } catch (NoSuchMethodException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            System.out.println(e.getMessage());
        }
    }
}

 

未完带续

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: public void getAnnotation(Class clazz) { Annotation[] annotations = clazz.getAnnotations(); for (Annotation annotation : annotations) { System.out.println("注解名称:" + annotation.annotationType().getSimpleName()); } } ### 回答2: Java反射机制可以用来获取类、方法、属性等的注解信息。下面是一个简单的示例代码,用于演示如何使用反射获取注解: ```java import java.lang.annotation.Annotation; import java.lang.reflect.Method; @MyAnnotation(name = "TestClass") public class TestClass { @MyAnnotation(name = "TestMethod") public void testMethod() { System.out.println("This is a test method"); } public static void main(String[] args) { Class<TestClass> clazz = TestClass.class; Annotation[] classAnnotations = clazz.getAnnotations(); for (Annotation annotation : classAnnotations) { if (annotation instanceof MyAnnotation) { MyAnnotation myAnnotation = (MyAnnotation) annotation; System.out.println("Class Annotation Name: " + myAnnotation.name()); } } Method[] methods = clazz.getDeclaredMethods(); for (Method method : methods) { Annotation[] methodAnnotations = method.getAnnotations(); for (Annotation annotation : methodAnnotations) { if (annotation instanceof MyAnnotation) { MyAnnotation myAnnotation = (MyAnnotation) annotation; System.out.println("Method Annotation Name: " + myAnnotation.name()); } } } } } @interface MyAnnotation { String name(); } ``` 在上面的代码中,我们首先定义了一个注解 `@MyAnnotation`,并将其分别应用到 `TestClass` 类和 `testMethod` 方法上。然后,通过反射获取 `TestClass` 类的注解和 `testMethod` 方法的注解,并将其输出到控制台上。 从输出结果来看,我们可以得到类注解的名称为 "TestClass",方法注解的名称为 "TestMethod"。 注意,以上只是一个简单的示例,实际使用反射获取注解时,可能需要结合具体的业务逻辑进行处理。 ### 回答3: 反射Java中强大的特性之一,可以在运行时动态获取类的信息,并且可以通过反射机制获取类、方法、字段等的注解信息。 下面是一个示例代码,用于演示如何使用Java反射获取注解: ```java import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.lang.reflect.Method; // 定义一个注解 @interface MyAnnotation { String value() default ""; } // 定义一个类并添加注解 @MyAnnotation(value = "这是一个自定义注解") class MyClass { @MyAnnotation(value = "这是一个字段注解") private String myField; @MyAnnotation(value = "这是一个方法注解") public void myMethod() { System.out.println("Hello, Reflection!"); } } public class ReflectionDemo { public static void main(String[] args) { // 获取注解 Class<MyClass> clazz = MyClass.class; MyAnnotation classAnnotation = clazz.getAnnotation(MyAnnotation.class); if (classAnnotation != null) { System.out.println("类注解值:" + classAnnotation.value()); } // 获取字段注解 try { Field field = clazz.getDeclaredField("myField"); MyAnnotation fieldAnnotation = field.getAnnotation(MyAnnotation.class); if (fieldAnnotation != null) { System.out.println("字段注解值:" + fieldAnnotation.value()); } } catch (NoSuchFieldException e) { e.printStackTrace(); } // 获取方法注解 try { Method method = clazz.getMethod("myMethod"); MyAnnotation methodAnnotation = method.getAnnotation(MyAnnotation.class); if (methodAnnotation != null) { System.out.println("方法注解值:" + methodAnnotation.value()); } } catch (NoSuchMethodException e) { e.printStackTrace(); } } } ``` 在上面的代码中,我们首先定义了一个自定义注解`MyAnnotation`,并在`MyClass`类的类声明、字段和方法上添加了该注解。然后使用反射机制获取注解、字段注解和方法注解,并从中获取注解的值打印输出。 运行上述代码,输出结果如下: ``` 类注解值:这是一个自定义注解 字段注解值:这是一个字段注解 方法注解值:这是一个方法注解 ``` 以上就是使用Java反射获取注解的示例代码。通过这种方式,我们可以在运行时动态地获取类的注解信息,为程序的灵活性和扩展性提供了很大的便利。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值