Java 註解 Annotation自定義實戰

概念:

Java 提供的一種原程序中的元素關聯任何信息和任何元數據的途徑和方法

內容:

  1. Java 中常見的註解
  2. 註解分類
  3. 自定義註解
  4. 註解應用實戰

一、Java 中常見的註解

1、JDK 自帶註解

@Override 覆蓋
@Deprecated 廢棄
@SuppressWarnings 抑制警告

示例代碼:

AnnotationDemo.java

package demo;


interface Human{
    public void sayHello();
    public void sayHi();
}


class Person implements Human{

    @Override
    public void sayHello() {
        System.out.println("hello");
    }

    @Deprecated
    @Override
    public void sayHi() {
        System.out.println("hi");
    }
}


public class AnnotationDemo {
    public static void main(String[] args) {
        Person person = new Person();
        person.sayHi();
    }
}

以上代碼執行編譯的時候會有提示

$ javac AnnotationDemo.java
注: AnnotationDemo.java使用或覆蓋了已過時的 API。
注: 有關詳細信息, 請使用 -Xlint:deprecation 重新編譯。


$ javac AnnotationDemo.java -Xlint:deprecation
AnnotationDemo.java:28: 警告: [deprecation] Person中的sayHi()已過時
        person.sayHi();
              ^
1 個警告

可以抑制警告

public class AnnotationDemo {
    @SuppressWarnings("deprecation")
    public static void main(String[] args) {
        Person person = new Person();
        person.sayHi();
    }
}

再次編譯就沒有提示了

2、常見的第三方註解

Spring

@Autowired
@Service
@Repository

MyBatis

@InsertProvider
@UpdateProvider
@Options

二、註解分類

1、按照運行機制分

  1. 源碼註解 註解只存在於源碼中,編譯後成.class 文件就不存在了

  2. 編譯時註解 註解在源碼和.class 文件都存在

  3. 運行時註解 在運行階段還起作用,會影響運行邏輯
    eg: @Autowired

2、按照來源分

  1. JDK 註解
  2. 第三方註解
  3. 自定義註解

元註解:註解的註解

三、自定義註解

1、自定義註解語法

示例及說明

package demo;

import java.lang.annotation.*;

/**
 * Target是註解作用域:
 *  TYPE 類,接口
 *  FIELD 字段聲明
 *  METHOD 方法聲明
 *  PARAMETER 參數聲明
 *  CONSTRUCTOR 構造方法聲明
 *  LOCAL_VARIABLE 局部變量聲明
 *  ANNOTATION_TYPE
 *  PACKAGE 包聲明
 *
 * Retention 生命週期
 *  SOURCE 只在源碼顯示,編譯時會丟棄
 *  CLASS 編譯時會記錄到class中,運行時忽略
 *  RUNTIME 運行時存在,可以通過反射讀取
 *
 * Inherited 允許子類繼承
 *
 * Documented 生成javadoc時會包含註解信息
 */
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
// 1、使用關鍵詞@interface 定義註解
public @interface Description {
    // 2、成員以無參無異常方式聲明
    String desc();

    // 3、可以用default爲成員指定默認值
    int age() default 18;

    // 4、成員類型是受限的,合法的類型包括原始類型及
    // String, Class, Annotation, Enumeration
    String author();
}
/**
 * 5、如果註解只有一個成員,則成員名必須取名爲: value()
 * 在使用時可以忽略成員名和賦值號(=)
 *
 * 6、註解類可以沒有成員,沒有成員的註解稱爲 標識註解
 */

2、使用註解的語法

// @<註解名>(<成員名>=<成員值>...)

class Demo {
    @Description(desc = "i am  eyeColor", author = "Tom", age = 18)
    public String eyeColor() {
        return "red";
    }

    public static void main(String[] args) {

    }
}

3、解析註解

概念:

通過反射獲取類、函數或成員上運行時註解信息,從而實現動態控制程序運行的邏輯

package demo;

import java.lang.annotation.*;
import java.lang.reflect.Method;

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

@Description("a class annotation")
class Demo {
    @Description("a method annotation")
    public String eyeColor() {
        return "red";
    }
}

class DemoTest {
    public static void main(String[] args) throws ClassNotFoundException {
        // 1、使用類加載器
        Class clazz = Class.forName("demo.Demo");

        // 2、找到類上面的註解
        boolean isExist = clazz.isAnnotationPresent(Description.class);
        if (isExist) {
            // 3、拿到註解實例
            Description description = (Description) clazz.getAnnotation(Description.class);
            System.out.println(description.value());
            // a class annotation
        }

        // 找到方法上的註解
        Method[] methods = clazz.getMethods();
        for (Method method : methods) {
            boolean isMethodExist = method.isAnnotationPresent(Description.class);
            if (isMethodExist) {
                Description description = (Description) method.getAnnotation(Description.class);
                System.out.println(description.value());
            }
        }

        // 另一種解析方法
        for (Method method : methods) {
            Annotation[] annotations = method.getAnnotations();
            for (Annotation annotation : annotations) {
                if (annotation instanceof Description) {
                    Description description = (Description) annotation;
                    System.out.println(description.value());
                }
            }
        }
    }
}

4、繼承@Inherited

package demo;

import java.lang.annotation.*;
import java.lang.reflect.Method;

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

@Description("a class annotation")
class Person {
    @Description("a method annotation")
    public String eyeColor() {
        return "red";
    }
}


class Child extends Person {
    @Override
    public String eyeColor() {
        return "red";
    }
}

// Child 繼承 Person 可以獲取類上面的註解

四、註解實戰

1、項目說明

用註解實現持久層框架,替代 Hibernate 解決方案

2、需求:

  1. 有一張用戶表,字段包括用戶用戶名,年齡,電話
  2. 方便對每個對象進行保存,並打印出 SQL。
  3. 使用方式要足夠簡單,見代碼示例。

3、思路:

  1. 考慮代碼如何與數據庫進行映射
  2. 實現 save

4、代碼實現

文件目錄

.
├── Column.java
├── Demo.java
├── Table.java
└── User.java

Table.java

package anno;

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();
}

Column.java

package anno;

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();
}

User.java

package anno;

@Table("user")
public class User {
    @Column("name")
    private String name;

    @Column("age")
    private Integer age;

    @Column("phone_number")
    private String phoneNumber;

    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 getPhoneNumber() {
        return phoneNumber;
    }

    public void setPhoneNumber(String phoneNumber) {
        this.phoneNumber = phoneNumber;
    }
}

Demo.java

package anno;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class Demo {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException {
        User user = new User();
        user.setAge(12);

        System.out.println(save(user));
        //insert into `user` (`name`, `phone_number`, `age`) values('null', 'null', '12')
    }

    private static String save(User user) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        // 獲取class
        Class clazz = user.getClass();

        // 獲取表名
        if (!clazz.isAnnotationPresent(Table.class)) {
            return null;
        }

        StringBuilder builder = new StringBuilder();
        builder.append("insert into ");

        Table table = (Table) clazz.getAnnotation(Table.class);
        String tableName = table.value();
        builder.append("`").append(tableName).append("` ");

        // 獲取所有字段名和字段值
        Map<String, String> map = new HashMap<>();

        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            if (!field.isAnnotationPresent(Column.class)) {
                continue;
            }

            // 通過註解獲取字段名稱
            Column column = field.getAnnotation(Column.class);
            String columnName = column.value();

            // 通過反射獲取字段的值
            String fieldName = field.getName();
            String methodName = "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
            Method method = clazz.getMethod(methodName);
            String columnValue = String.valueOf(method.invoke(user));

            map.put(columnName, columnValue);
        }

        // 拼裝sql
        builder.append("(");
        Object[] columns = map.keySet().toArray();
        for (int i = 0; i < columns.length; i++) {
            builder.append("`").append(columns[i].toString()).append("`");
            if (i != columns.length - 1) {
                builder.append(", ");
            }
        }

        builder.append(") ");

        builder.append("values");

        builder.append("(");
        Object[] values = map.values().toArray();
        for (int i = 0; i < values.length; i++) {
            builder.append("'").append(values[i].toString()).append("'");
            if (i != values.length - 1) {
                builder.append(", ");
            }
        }
        builder.append(") ");

        return builder.toString();
    }
}

五、總結

1、認識註解

2、註解的作用範圍@Target 和生命週期@Retention

(1)作用範圍: 包、類、字段、方法、方法參數、局部變量

(2)生命週期:源文件 source,編譯 class, 運行 runtime

3、讀懂註解

4、使用註解解決實際開發問題

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章