java基础(注解)

导读.png

注解基本概念

我们在开发当中经常看到一些注解,例如override,Deprected等,这些注解再常见不过了,但是这些注解到底有什么作用呢?在spring 框架中大量的使用注解,那么它的工作原理又是什么呢?接下来我们来分析一下把。

概念

  • 注解即元数据,就是源代码的元数据
  • 注解在代码中添加信息提供了一种形式化的方法,可以在后续中更方便的 使用这些数据
  • Annotation是一种应用于类、方法、参数、变量、构造器及包声明中的特殊修饰符。它是一种由JSR-175标准选择用来描述元数据的一种工具。

作用

  • 生成文档
  • 跟踪代码依赖性,实现替代配置文件功能,减少配置。如Spring中的一些注解
  • 在编译时进行格式检查,如@Override等

意义

  • 注解之前,XML被广泛的应用于描述元数据,XML的维护越来越糟糕
    在需要紧耦合的地方,比XML该容易维护,阅读更方便
  • 在需要比较多参数设置时,使用xml更方便,而在将某个方法声明为服务时这种紧耦合的情况下,比较适合注解
  • XML是松耦合的,注解是紧耦合的
  • 对于XML和注解的使用,要具体问题具体分析
  • Java的annotation没有行为,只能有数据,实际上就是一组键值对而已。通过解析(parse)Class文件就能把一个annotation需要的键值对都找出来

分类

按照运行机制来分类

1.源码注解

只在源码中出现,编译成class文件就不存在了

2.编译时注解

注解在源码和编译中都存在 例如:Override,Deprected,SuppressWarnings

3.运行时注解

运行阶段起作用,甚至会影响运行逻辑的注解 例如spring框架中的@Autowired

按照来源来分类

  1. 来自Jdk的注解
  2. 来自第三方的注解
  3. 我们自己定义的注解

JDK注解

  • Override: 保证编译时 要重写方法的正确性
  • Deprected: 提示该方法已经过时
  • SuppressWarnings: 关闭特定警告信息

Java元注解

java中元注解有四个: @Retention @Target @Document @Inherited;它负责注解其他注解

@Retention:注解的保留位置

  • @Retention(RetentionPolicy.SOURCE) //注解仅存在于源码中,在class字节码文件中不包含
  • @Retention(RetentionPolicy.CLASS) // 默认的保留策略,注解会在class字节码文件中存在,但运行时无法获得,
  • @Retention(RetentionPolicy.RUNTIME) // 注解会在class字节码文件中存在,在运行时可以通过反射获取到

@Target: 注解的作用目标

  • @Target(ElementType.TYPE) //接口、类、枚举、注解
  • @Target(ElementType.FIELD) //字段、枚举的常量
  • @Target(ElementType.METHOD) //方法
  • @Target(ElementType.PARAMETER) //方法参数
  • @Target(ElementType.CONSTRUCTOR) //构造函数
  • @Target(ElementType.LOCAL_VARIABLE)//局部变量
  • @Target(ElementType.ANNOTATION_TYPE)//注解
  • @Target(ElementType.PACKAGE) ///包

@Document:说明该注解将被包含在javadoc中

@Inherited:说明子类可以继承父类中的该注解

自定义注解

概念就说这么多了,看多了没实践会有点懵,注解到底有什么用啊?注解是怎么使用的啊?接下来我们带着这一系列的疑问来做一个简单的例子。来梳理一下我们疑问

需求

我们模仿一下Hibernate的注解。只通过注解来简单实现它的核心功能

我们在数据库有一张user表。用于存储用户信息,然后我们现在对这张表进行查询功能。使用注解的方式来打印出不同条件来查询表的SQL语句。

准备

我们先准备好数据库中的表对应的bean对象

@Table("user")//自定义注解表名
public class UserBean {
    @Column("id")//自定义注解属性名
    private int id;
    @Column("name")
    private String name;
    @Column("age")
    private int age;
    @Column("sex")
    private String sex;

    public int getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }
}

第一步:定义自己的注解

我们使用元注解对这个自己的注解来定义

表名对于的注解@Table
@Target(ElementType.TYPE)//作用在接口、类、枚举、注解
@Retention(RetentionPolicy.RUNTIME)//运行时的注解
public @interface Table {
    String value();
}

字段名对应的注解@Column
@Target(ElementType.FIELD)//字段、枚举的常量
@Retention(RetentionPolicy.RUNTIME)//运行时的注解
public @interface Column {
    String value();
}

第二步:获取解析注解

  • 使用注解的过程,重要的是创建注解处理器
  • Java SE5 扩展了反射机制的API,以帮助程序员快速的构造自定义注解处理器
  • 注解处理器类库: java.lang.reflect.AnnotatedElement
  1. 获取对象的class
  2. 判断这个对象是否有表名的注解
  3. 获取表名注解的值
  4. 获取对象的所有属性
  5. 遍历所有属性判断是否有字段名的注解
  6. 获取各个字段名的注解的属性值
  7. 拼接sql语句
 public static String querry(Object o) {
        StringBuffer sb = new StringBuffer();
        //获取这个对象的class
        Class oClass = o.getClass();
        //判断这个对象中有没有Table注解
        boolean isAnnotation = oClass.isAnnotationPresent(Table.class);
        if (!isAnnotation) {
            throw new NullPointerException("Annotation not found");
        }
        //获取Table注解
        Table table = (Table) oClass.getAnnotation(Table.class);
        //获取Table注解的值
        String tableName = table.value();
        //组装sql语句
        sb.append("select * from ").append(tableName).append(" where ");
        //获取对象中的所有属性
        Field[] fields = oClass.getDeclaredFields();
        //逐个遍历属性
        for (Field field : fields) {
            //判断这个属性中是否有Column这个注解
            if (!field.isAnnotationPresent(Column.class)) {
                continue;
            }
            //获取Column注解
            Column column = field.getAnnotation(Column.class);
            //获取去Column中的值
            String name = column.value();

            Object value = "";
            try {
                //获取对应属性的值
                Method method = oClass.getMethod("get" + field.getName().substring(0, 1).toUpperCase() + field.getName().substring(1));
                value = method.invoke(o);
            } catch (Exception e) {
                e.printStackTrace();
            }
            //如果属性值为空或者为0
            if (value == null || (value instanceof Integer && (Integer) value == 0)) {
                continue;
            }
            //如果为string类型拼接’
            if (value instanceof String) {
                sb.append(name + "=").append("'").append(value).append("'").append(",");
            } else if (value instanceof Integer) {
                sb.append(name + "=").append(value).append(",");
            }
        }
        sb.deleteCharAt(sb.length() - 1);

        return sb.toString();
    }

第三步 运行测试

测试代码

  public static void main(String[] args) {
        UserBean userBean = new UserBean();
        userBean.setSex("男");
        userBean.setAge(20);
        String sql1 = querry(userBean);
        System.out.println(sql1);
        UserBean userBean2 = new UserBean();
        userBean2.setSex("男");
        userBean2.setName("张三");
        String sql2 = querry(userBean2);
        System.out.println(sql2);
    }

运行结果

  • select * from user where age=20,sex='男'
  • select * from user where name='张三',sex='男'

好了到了这里看来我们的自定义注解已经成功了。我们通过自己定义了两个注解Table和Column分别表示表明和属性名。然后再通过反射拿到对应的注解值和属性值,最后拼接成一条Sql。

注解的优缺点及与XML的比较

优:

  • 方便,简洁,配置信息和 Java 代码放在一起,有助于增强程序的内聚性
  • 若要对配置项进行修改,不得不修改 Java 文件,重新编译打包应用

缺:

  • 分散到各个class文件中,维护性较差
  • 配置项编码在 Java 文件中,可扩展性差

与XML比较:

  • 简洁
  • 没有XML配置更强大
  • 不便于修改,不便于统一管理
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,980评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,178评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,868评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,498评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,492评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,521评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,910评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,569评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,793评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,559评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,639评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,342评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,931评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,904评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,144评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,833评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,350评论 2 342

推荐阅读更多精彩内容