全面解析Java注解

注解說(shuō)明

Java提供了一種原程序中的元素關(guān)聯(lián)任何信息和任何元數(shù)據(jù)的途徑和方法。
JDK1.5版本引入的一個(gè)特性,可以聲明在包、類(lèi)、屬性、方法、局部變量、方法參數(shù)等的前面,用來(lái)對(duì)這些元素進(jìn)行說(shuō)明、注釋?zhuān)梢岳斫鉃樽⒔饩褪且粋€(gè)標(biāo)記

好處

1.能夠讀懂別人寫(xiě)的代碼,特別是框架相關(guān)的代碼
2.讓編程更加簡(jiǎn)潔,代碼更加清晰

分類(lèi)

運(yùn)行機(jī)制劃分:源碼注解、編譯時(shí)注解、運(yùn)行時(shí)注解
按照來(lái)源劃分:JDK注解、第三方注解、自定義注解
元注解:定義注解的注解

常見(jiàn)第三方注解

通常在框架中出現(xiàn),如SpringMVC、Spring、Mybatis

分類(lèi)解析

源碼注解:注解只在源碼中存在,編譯成 .class 文件就不存在了,如@Override,@SuppressWarnings
編譯時(shí)注解:注解在源碼和 .class 文件中都存在
運(yùn)行時(shí)注解:在運(yùn)行階段還起作用,甚至?xí)绊戇\(yùn)行邏輯的注解,如spring的@Autowired,jdk的@Deprecated

jdk注解

@Override

作用是表示該方法是重寫(xiě)或覆蓋父類(lèi)的方法聲明
使用@Override注解的方法必須重寫(xiě)父類(lèi)或者java.lang.Object中的一個(gè)同名方法

@Deprecated

表示該方法已過(guò)時(shí),或者說(shuō)有更好的方法取代了它
如果使用過(guò)時(shí)的方法,會(huì)報(bào)警告信息 The method xxx from the type xxx is deprecated

@SuppressWarnings

該批注的作用是給編譯器一條指令,告訴它對(duì)被批注的代碼元素內(nèi)部的某些警告保持靜默
@SuppressWarnings("deprecation") 如使用了過(guò)時(shí)方法加上該注解即可消除警告信息

自定義注解

語(yǔ)法說(shuō)明
  1. 使用@interface關(guān)鍵字定義注解,也稱(chēng)為注釋類(lèi)型 public @interface XXX{}
  2. 成員以無(wú)參無(wú)異常方式聲明,如String desc();
  3. 可以使用default為成員指定一個(gè)默認(rèn)值,如int age() default 18;
  1. 成員類(lèi)型是受限的,合法類(lèi)型包括原始類(lèi)型及String、ClassAnnotation、Enumeration
  2. 如果注解只有一個(gè)成員,則成員名必須取名為value(),在使用時(shí)可以忽略成員名和賦值號(hào)(=),直接@XXX(val)
  3. 注解類(lèi)可以沒(méi)有成員,沒(méi)有成員的注解稱(chēng)為標(biāo)識(shí)注解,如:@Autowired
@Target元注解

語(yǔ)法: @target(ElementType[] value)
如: @Target({ElementType.METHOD,ElementType.TYPE})
作用:指示該注解(注釋類(lèi)型)所適用的程序元素的種類(lèi),如果沒(méi)有該元注解,則聲明的類(lèi)型可以用在任一程序元素上

ElementType

枚舉類(lèi),程序元素類(lèi)型,配合target注解以指定在什么情況下使用注釋類(lèi)型是合法的,取值如下

@Retention元注解

語(yǔ)法:@Retention(RetentionPolicy value)
如: @Retention(RetentionPolicy.RUNTIME)
作用:指示注釋類(lèi)型的注釋要保留多久,如果沒(méi)有該元注解,則保留策略默認(rèn)為 RetentionPolicy.CLASS

RetentionPolicy

枚舉類(lèi),注釋保留策略,取值如下
CLASS 編譯器將把注釋記錄在類(lèi)文件中,但在運(yùn)行時(shí) VM 不需要保留注釋
RUNTIME 編譯器將把注釋記錄在類(lèi)文件中,在運(yùn)行時(shí) VM 將保留注釋?zhuān)虼丝梢酝ㄟ^(guò)反射讀取
SOURCE 只在源碼顯示,編譯時(shí)會(huì)丟棄

標(biāo)識(shí)元注解
@Inherited

允許子類(lèi)繼承父類(lèi),對(duì)于實(shí)現(xiàn)接口的子類(lèi)將不會(huì)繼承父類(lèi)注釋類(lèi)型,對(duì)于子類(lèi)重寫(xiě)的方法將不會(huì)繼承父類(lèi)該方法的注釋類(lèi)型

@Documented

生成javadoc時(shí)會(huì)包含注解信息(在Eclipse中項(xiàng)目右鍵-->export-->javadoc)

使用注解語(yǔ)法

@<注解名>(<成員名1>=<成員值1>,<成員名2>=<成員值2>,...)
注意:注解上定義的全部成員使用時(shí)都必須指明(有默認(rèn)值的成員除外)

自定義注解示例
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Description {

    String desc();
    String author();
    int age() default 18;
}

//在某個(gè)類(lèi)的方法中使用該注解
@Description(desc = "I am eyeColor", author = "silly", age = 22)
public String eyeColor(){
    return "red";
}

解析注解

通過(guò)反射獲取類(lèi)、函數(shù)或成員上的運(yùn)行時(shí)注解信息,從而實(shí)現(xiàn)動(dòng)態(tài)控制程序運(yùn)行的邏輯

相關(guān)API

以下是Class類(lèi)、Method類(lèi)、Field類(lèi)所共有方法
Annotation對(duì)象獲取后,使用該對(duì)象.成員即可獲取注解上的成員值

解析示例

有如下自定義注解及使用注解的類(lèi)

@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Description {

    String desc();
    String author() default "ruoxiyuan";
    int age() default 18;
}

package com.rxy.annotation;

@Description(author = "dubbo", desc = "I am person")
public class Person {
    
    @Description(author = "zookeeper", desc = "I am person method")
    public String name(){
        return null;
    }
    
    public int age(){
        return 0;
    }
}

package com.rxy.annotation;

@Description(desc = "I am class Annotation")
public class Child extends Person {

    @Override
    @Description(author = "activeMQ", desc = "I am method Annotation", age=20)
    public String name() {
        return null;
    }

    @Override
    public int age() {
        return 0;
    }
}

對(duì)注解類(lèi)進(jìn)行注解解析

/**
 * 注解解析類(lèi)
 */
public class ParseAnn {

    public static void main(String[] args) {
        //獲取類(lèi)上的注解
        try {
            //1.使用類(lèi)加載器加載類(lèi),獲取字節(jié)碼對(duì)象
            Class clazz = Class.forName("com.rxy.annotation.Child");
            //2.判斷類(lèi)是否含有指定注解
            boolean isExist = clazz.isAnnotationPresent(Description.class);
            if(isExist){
                //獲取注解對(duì)象
                Description desc = (Description)clazz.getAnnotation(Description.class);
                //獲取注解成員值
                System.out.println(desc.author() + "," + desc.desc() + "," + desc.age());
            }
            
            //獲取方法上的注解
            Method[] methods = clazz.getMethods();
            for(Method m : methods){
                boolean isMExist = m.isAnnotationPresent(Description.class);
                if(isMExist){
                    Description d = m.getAnnotation(Description.class);
                    System.out.println(m.getName()+ ":" + d.author() + "," + d.desc() + "," + d.age());
                }
            }
            
            //另一種解析方式
            for(Method m : methods){
                Annotation[] as = m.getDeclaredAnnotations();
                for(Annotation a : as){
                    if(a instanceof Description){
                        Description d = (Description)a;
                        System.out.println(d.author() + "," + d.desc() + "," + d.age());
                    }
                }
            }
            
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        /* 打印結(jié)果
         * ruoxiyuan,I am class Annotation,18
         * name:activeMQ,I am method Annotation,20
         * activeMQ,I am method Annotation,20 
         */
        
        /*
         * 將Child中類(lèi)與方法中的注解全部注釋
         * 此時(shí)打印結(jié)果為
         * dubbo,I am person,18
         */
        
        /*
         * 將Child中類(lèi)與方法中的注解全部注釋?zhuān)瑢ame方法也注釋
         * 此時(shí)打印結(jié)果為
         * dubbo,I am person,18
         * name:zookeeper,I am person method,18
         * zookeeper,I am person method,18
         */ 
    }
}

綜合實(shí)戰(zhàn)

項(xiàng)目取自一個(gè)公司的持久層架構(gòu),用來(lái)代替Hibernate的解決方案,核心代碼就是通過(guò)注解來(lái)實(shí)現(xiàn)的。
需求:
1.有一張用戶(hù)表,字段包括用戶(hù)ID,用戶(hù)名,昵稱(chēng),年齡,性別,所在城市,郵箱,手機(jī)號(hào)
2.方便的對(duì)每個(gè)字段或字段的組合條件進(jìn)行檢索,并打印出SQL
3.使用方式要足夠簡(jiǎn)單

定義注解

自定義注解用于映射類(lèi)與表、屬性與表字段的對(duì)應(yīng)關(guān)系

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Table {

    String value();
}

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Column {

    String value();
}
實(shí)體類(lèi)

實(shí)體類(lèi)可以有多個(gè),此處以用戶(hù)表為例

@Table("user")
public class Filter {
    //省略getset方法
    @Column("id")
    private int id;

    @Column("user_name")
    private String userName;

    @Column("nick_name")
    private String nickName;

    @Column("age")
    private int age;

    @Column("city")
    private String city;

    @Column("email")
    private String email;

    @Column("mobile")
    private String mobile;
}
定義注解解析類(lèi)
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class Test {

    public static void main(String[] args) {
        //測(cè)試
        Filter f1 = new Filter();
        f1.setId(10);// 查詢(xún)id為10的用戶(hù)
        Filter f2 = new Filter();
        f2.setAge(18);
        f2.setUserName("lucy");// 查詢(xún)用戶(hù)名為lucy年齡18的用戶(hù)
        Filter f3 = new Filter();
        f3.setEmail("liu@sina.com,zh@163.com,77@qq.com");// 查詢(xún)郵箱為其中任意一個(gè)的用戶(hù)

        String sql1 = query(f1);
        String sql2 = query(f2);
        String sql3 = query(f3);

        System.out.println(sql1);
        System.out.println(sql2);
        System.out.println(sql3);
        /*select * from student where 1=1 and id=10
        select * from student where 1=1 and user_name='lucy' and age=18
        select * from student where 1=1 and email in('liu@sina.com','zh@163.com','77@qq.com')*/

    }
    /**
     * 該方法可以對(duì)多張表對(duì)象進(jìn)行通用解析
     * @param f
     * @return
     */
    @SuppressWarnings("unchecked")
    private static String query(Object f) {
        StringBuilder sb = new StringBuilder();
        // 1.獲取字節(jié)碼對(duì)象
        Class clazz = f.getClass();
        // 2.獲取table名稱(chēng)
        boolean isExist = clazz.isAnnotationPresent(Table.class);
        if (!isExist) {
            return null;
        }
        Table t = (Table) clazz.getAnnotation(Table.class);
        String tableName = t.value();
        sb.append("select * from ").append(tableName).append(" where 1=1");
        // 3.遍歷所有字段
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            // 4.處理每個(gè)字段對(duì)應(yīng)的sql
            boolean isFExist = field.isAnnotationPresent(Column.class);
            if (!isFExist) {
                continue;
            }
            // 4.1獲取字段名稱(chēng)
            Column col = field.getAnnotation(Column.class);
            String colName = col.value();
            // 4.2獲取字段值
            String fieldName = field.getName();
            String getMethodName = "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
            Object fieldValue = null;
            try {
                Method method = clazz.getMethod(getMethodName);
                fieldValue = method.invoke(f, null);
            } catch (Exception e) {
                e.printStackTrace();
            }
            // 4.3拼接sql
            if (fieldValue == null || (fieldValue instanceof Integer && (Integer) fieldValue == 0)) {
                continue;
            }
            sb.append(" and ").append(colName);
            if (fieldValue instanceof String) {
                if (((String) fieldValue).contains(",")) {
                    String[] values = ((String) fieldValue).split(",");
                    sb.append(" in(");
                    for (String s : values) {
                        sb.append("'").append(s).append("'").append(",");
                    }
                    sb.deleteCharAt(sb.length() - 1);
                    sb.append(")");
                } else {
                    sb.append("=").append("'").append(fieldValue).append("'");
                }
            } else {
                sb.append("=").append(fieldValue);
            }
        }
        return sb.toString();
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容