【Java】看完本篇文章,你就能自定义注解了

——————————————分割线

Java实战之Spring Boot入门到精通


——————————————分割线
该商品与本文无关

一、什么是注解

注意!是注“解”,不是注释,注释是在写代码时加上一些对代码的解释,它们两都起到了解释代码的作用

1.概念

从JDK5开始,Java增加对元数据的支持,也就是注解,注解与注释是有一定区别的,可以把注解理解为代码里的特殊标记,这些标记可以在编译,类加载,运行时被读取,并执行相应的处理。通过注解开发人员可以在不改变原有代码和逻辑的情况下在源代码中嵌入补充信息。
——百度百科

在Java中的注解是以@开头的,它会在编译,类加载,运行时被读取,然后由注解处理器来进行处理,注解仅仅是存放元数据,不会修改修饰对象的代码产生直接影响
注解和类、接口、数组、枚举等关键字平起平坐,同样重要

2.来源

在注解还没有出现之前,一般使用XML文件来存储元数据的,配置文件的信息还有SQL语句都可以算是元数据,这就使元数据和代码耦合度十分的低,如果项目十分大的话,就会造成维护困难,于是就诞生了注解,让元数据和代码紧密结合,极大的降低了编程的复杂度,在后来,注解功能就不仅仅局限与绑定元数据了
注解是真的好用,谁用谁知道

3.注解分类

  1. JDK自带的注解
  2. 来自第三方框架的注解
  3. 自定义注解

二、自定义注解

在上面三类注解中,我们主要关注的是自定义注解

1.元注解

元注解在自定义注解中需要用到,总共有4个,分别是@Target、@Retention、@Inherited、@Documented

@Target

这个注解是表示自定义注解的作用域,也就是说这个注解表明了定义的注解可以用来修饰什么类型的类、属性、方法

下面是 @Target的取值表 ,也不需要记,用的时候查看一下就好了

取值注解使用范围
METHOD可用于方法上
TYPE可用于类或者接口上
ANNOTATION_TYPE可用于注解类型上(被@interface修饰的类型)
CONSTRUCTOR可用于构造方法上
FIELD可用于域上,就是类的属性/字段
LOCAL_VARIABLE可用于局部变量上,就是方法内部的属性
PACKAGE用于记录java文件的package信息
PARAMETER可用于参数上

@Resource中的@Target的value

@Target({TYPE, FIELD, METHOD})

@Retention

这个注解表示的是自定义注解的生命周期,也就是说这个注解能够决定自定义注解能够在那个阶段被处理,在那些阶段可以存在

下面是**@Retention的取值表**,同上

取值生命周期
RetentionPolicy.SOURCE只能存活在.java文件上,不会存活在.class文件,在经过编译器编译之后就不存在了
RetentionPolicy.CLASS能够存活在.class文件,不会存活在运行时期,也就是在内存中,但是在经过类加载器之后就不存在了
RetentionPolicy.RUNTIME能够存活在内存中,无论经过编译器还是类加载器都会存在,也是我们自定义注解时常用的

@Resource中的@Retention的value

@Retention(RUNTIME)

@Inherited

这个注解能够让子类继承父类上的注解,打个比方说,A继承了B,B上有这个注解,也有其他注解,那么A类就会继承B类上的其他注解。但是子接口的继承和实现都不会父接口的注解
这个注解没有value,直接使用就好了

@Documented

这个注解表示,在生成javadoc时,带了该注解的类是否要包含该自定义注解的信息,我们用不上。生成javadoc文档的方式有很多,用IDEA、javadoc.exe命令行

2.自定义注解语法

  1. 使用@interface关键字定义,跟使用class、interface、enum等关键字一样
  2. 定义注解类型元素(annotation type element)
    定义注解类型元素才是我们的重点,第一,元素的访问修饰符必须为public;第二,元素的类型只能为基本数据类型(String、int、Class等)、还有枚举类型;第三、如果只有一个元素,则把元素名称取为value;第四,在元素名称后面加**();第五,在()后面可以加default,后面接默认值**

拉个@Retention注解来作例子

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
    /**
     * Returns the retention policy.
     * @return the retention policy
     */
    RetentionPolicy value();
}

3.自定义注解

场景:类似于数据库的表格
Table

/**
 * @author xxj
 * 表注解
 */
@Target({TYPE})
@Retention(RUNTIME)
public @interface Table {
    String value();
}

Column

/**
 * @author xxj
 * 行注解
 */
@Target(LOCAL_VARIABLE)
@Retention(RUNTIME)
public @interface Column {
    String value();
}

4.使用自定义注解

定义了注解,当然也要使用注解啦,这里涉及到反射机制,如果还不了解的可以去看我的【java】反射机制

首先,先定义一个User类

/**
 * @author xxj
 * User表
 */
@Table("User")
public class User implements Serializable {
    @Column("name")
    private String name;
    @Column("age")
    private Integer age;
    public User() {
    }
    //get、set、toString就不放上来了
}

然后,写一个Dome测试类
最重要的就是explainAnnotation()方法了,了解过反射的可以看一下

/**
 * @author xxj
 * 注解解析测试
 */
public class Demo {
    public static void main(String[] args) {
        User user=new User();
        user.setName("xiaoming");
        user.setAge(18);
        User user1=new User();
        user1.setName("xiaohong");
        //调用方法解析注解
        explainAnnotation(user);
        explainAnnotation(user1);
    }
    private static void explainAnnotation(Object object){
        //定义一个String来存放信息
        String str="";
        Class obClass=object.getClass();
        //判断是否是Table注解
        if (!obClass.isAnnotationPresent(Table.class)){
            return;
        }
        //获取到table注解信息
        Table table= (Table) obClass.getAnnotation(Table.class);
        //获取table注解的value
        String tableName=table.value();
        str+="tableName="+tableName;
        //获取object类的属性/字段
        Field[] fields=obClass.getDeclaredFields();
        //循环解析属性
        for (Field field:fields) {
            //判断是否是Column注解
            if (!field.isAnnotationPresent(Column.class)){
                continue;
            }
            //获取column注解信息
            Column column= field.getAnnotation(Column.class);
            //获取column注解的value
            String columnName=column.value();
            //拼接对应属性的getXxx()方法名称
            String getMethodName="get"+columnName.substring(0,1).toUpperCase()
                    +columnName.substring(1);
            //定义一个object对象来存放值
            Object fieldValue = null;
            try {
                //获取对应属性的getXxx()方法
                Method method=obClass.getMethod(getMethodName);
                //调用方法
                fieldValue=method.invoke(object);
            }catch (Exception e){}
            //拼接字符串
            str+=","+columnName+":"+fieldValue;
        }
        System.out.println(str);
    }
}

可以看到,注解的内容可以直接通过class类获得,但是对象的属性值只能通过对象获得,所以还是要使用method.invoke(object),将对象传递过去才能获得对象的属性值

三、总结

从上面的自定义注解和解析自定义注解中,我们可以看出创建了一个自定义的注解不是直接使用它就有效果的,而是要另外写一个方法来解析这个注解,然后根据解析出来的注解内容在做相应的操作。
像MyBatis中提供的@mapper,就是用来判断这个类是否是有@mapper注解,而写在@select上的value,就可以直接拿来拼接SQL语句了。

——————————————————————————————
你知道的越多,不知道的越多。

如果本文章内容有问题,请直接评论或者私信我。如果觉得写得还不错的话,点个赞也是对我的支持哦

未经允许,不得转载!

微信搜【程序员徐小白】,关注即可第一时间阅读最新文章。回复【面试题】有我准备的50道高频校招面试题,以及各种学习资料。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

程序员徐小白

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

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

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

打赏作者

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

抵扣说明:

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

余额充值