Java自定义Annotation并通过反射解析Annotation

本文详细介绍了Java中的注解(Annotation),包括自定义注解的创建、@Target和@Retention的使用,并展示了如何使用反射解析注解。此外,还通过示例展示了如何利用自定义注解生成查询SQL。
摘要由CSDN通过智能技术生成

一. 引言

对于使用过Spring的人来说,注解并不陌生,之前也写过一篇文章,介绍过注解。

https://blog.csdn.net/zxd1435513775/article/details/89973721

二. 自定义注解

1. 创建一个自定义的Annotation

@Documented
@Target(ElementType.METHOD) // 表明该注解只能用于修饰方法
@Retention(RetentionPolicy.RUNTIME)	// 该注解会在运行时有效(即运行时保留)
public @interface MethodInfo {
	// 以下是属性方法
    String author() default "dada";
    String version() default "1.0";
    String date();
    String comment();
}
  • @Target作用:用于描述注解的使用范围(即:被描述的注解可以用在什么地方)

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

@Target取值(ElementType)有:

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

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

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

@Retention取值(RetentionPoicy)有:

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

  • @Documented

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

  • @Inherited:

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

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

2. Java 内置的Annotation

从Java5版本开始,自带了三种标准annontation类型。

Override,java.lang.Override 是一个marker annotation类型,它被用作标注方法。它说明了被标注的方法重载了父类的方法,起到了断言的作用。如果我们使用了这种annotation在一个没有覆盖父类方法的方法时,java编译器将以一个编译错误来警示。这个annotaton常常在我们试图覆盖父类方法而确又写错了方法名时加一个保障性的校验过程。

Deprecated,Deprecated也是一种marker annotation。编译器将不鼓励使用这个被标注的程序元素。所以使用这种修饰具有一定的 “延续性”:如果我们在代码中通过继承或者覆盖的方式使用了这个过时的类型或者成员,虽然继承或者覆盖后的类型或者成员并不是被声明为 @Deprecated,但编译器仍然要报警。注意:@Deprecated这个annotation类型和javadoc中的 @deprecated这个tag是有区别的:前者是java编译器识别的,而后者是被javadoc工具所识别用来生成文档。

SuppressWarnings,此注解能告诉Java编译器关闭对类、方法及成员变量的警告。有时编译时会提出一些警告,对于这些警告有的隐藏着Bug,有的是无法避免的,对于某些不想看到的警告信息,可以通过这个注解来屏蔽。SuppressWarning不是一个marker annotation。它有一个类型为String[]的成员,这个成员的值为被禁止的警告名。对于javac编译器来讲,同时编译器忽略掉无法识别的警告名。

下面来使用下Java内置的 Annotation 和 自定义的Annotation

public class AnnotationExample {
    @Override
    @MethodInfo(author = "scorpios",version = "1.0",date = "2019/10/20",comment = "aaaa")
    public String toString() {
        return "AnnotationExample{}";
    }
}

使用反射来解析Annotation

注意:Annotation的Retention Policy 必须是RUNTIME,否则我们无法在运行时从该类里面获得任何数据

public class AnnotationParser {

    public static void main(String[] args) {
        // 获取类中的所有方法,并循环遍历每一个方法
        for (Method method: AnnotationExample.class.getMethods()) {
            // 判断方法是否标注MethodInfo注解
            if (method.isAnnotationPresent(MethodInfo.class)) {
                // 拿到注解信息,并遍历注解里面的数据
                for (Annotation annotation:method.getAnnotations()) {
                    System.out.println("------------------------");
                    System.out.println(annotation + " works on method:"+ method);
                }

                MethodInfo methodInfo = method.getAnnotation(MethodInfo.class);

                System.out.println(methodInfo);
                System.out.println(methodInfo.author());
                System.out.println(methodInfo.comment());
                System.out.println(methodInfo.date());
                System.out.println(methodInfo.version());

            }
        }
		System.out.println("--------------反射调用方法---------------");
        try {
            AnnotationExample example = new AnnotationExample();
            Class clazz = example.getClass();
            Method method = clazz.getDeclaredMethod("toString");

            String result = (String) method.invoke(example);
            System.out.println(result);
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}

输出结果:
在这里插入图片描述

三. 利用自定义注解自动生成查询SQL

@Entity("city")
public class City {

    private Integer id;
    private String name;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

public class CommonUtil {

    public static String buildQuerySqlForEntity(Object object){

        String sql = "select * from ";

        Class clazz = object.getClass();
        // 判断是否有注解
        if(clazz.isAnnotationPresent(Entity.class)){
            // 得到注解
            Entity entity = (Entity) clazz.getAnnotation(Entity.class);
            // 调用属性方法
            String value = entity.value();
            sql = sql + value;
        }
        return sql;
    }
}

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Entity {
    public String value() default "";
}
public static void main( String[] args ) {
    City city = new City();
    city.setId(1);
    city.setName("nanjing");
    String sql = CommonUtil.buildQuerySqlForEntity(city);
    System.out.println(sql);
}

在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

止步前行

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

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

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

打赏作者

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

抵扣说明:

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

余额充值