與架構師一步之差 —— 註解和反射

一、註解

簡介:註解(Annotation)是java5引入的一種代理輔助工具,它的核心作用是對類、方法】變量、參數和包進行標註,通過反射來訪問這些標註信息,以此在運行時改變所註解對象的行爲。java中的註解由內置註解和元註解組成。

元註解:元註解只要包括四個,用來修飾註解的作用。

  • @Retention定義註解的生命週期:【source -> class -> runtime】主要是作用在runtime時。
  • @Documented文檔註解,會被javadoc工具文檔化
  • @Inherited是否讓子類繼承該註解
  • @Target描述了註解的應用範圍。
    • type:表示可以用來修飾類、接口、註解類型或枚舉類型
    • PACKAGE:可以用來修飾包
    • PARAMETER:可以用來修飾參數
    • ANNOTATION_TYPE:可以用來修飾註解類型
    • METHOD:可以用來修飾方法
    • FIELD:可以用來修飾屬性(包括枚舉常量)
    • CONSTRUCTOR:可以用來修飾構造器
    • LOCAL_VARIABLE:可用來修飾局部變量

自定義一個註解示例:

/**
 * 自定義註解
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Table {

    String name();
}

二、反射

簡介:在運行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個對象,都能夠調用它的任意一個方法和屬性;這種動態獲取的信息以及動態調用對象的方法的功能稱爲Java語言的反射。

反射的優缺點:

  • 通過反射可以使程序代碼訪問裝載到JVM中的類的內部信息,獲取已裝載類的屬性信息,獲取已轉載類的方法,獲取已裝載類的構造方法信息
  • 反射提高了java程序的靈活性和擴展性,降低耦合性,提高自適應能力。
  • 反射會對性能造成一定的影響,同時讓代碼的可讀性變低。

常用的反射API:

方法名 返回值 參數描述
Class.forName(String) 獲取類的元信息 當前類文件的具體位置
類.getClass() 獲取類的元信息
class.getDeclaredFields() 獲取當前類中的所有屬性
setAccessible(true) 設置當前屬性爲可見 true或者false
getMethods() 獲取類所有方法
invoke(obj) 通過反射執行方法 類的元信息
getAnnotation 獲取註解 需要獲取的註解的Class

代碼示例:

public static void main(String[] args) throws Exception {
        Person p = new Person();
        p.setName("hdd");
        p.setAge(18);
        // 1.通過對象哪去反射對象
        Class<? extends Person> aClass = p.getClass();
        // 2.通過全類名拿取反射對象
        Class<?> aClass1 = Class.forName("com.study.cache.java.reflection.Person");
        System.out.println(aClass);
        System.out.println(aClass1);

        // 獲取類中所有屬性
        Field[] fields = aClass1.getDeclaredFields();
        for (Field field : fields) {
            // 相當於設置了屬性可見和可操作
            field.setAccessible(true);
            // 獲取字段名稱
            String name = field.getName();
            // 獲取對應字段設置的值
            Object o = field.get(p);
            System.out.println(name);
            System.out.println(o);

        }
        // 獲取類中所有方法
        Method[] methods = aClass1.getMethods();
        for (Method method : methods) {
            // 獲取方法名稱
            String name = method.getName();
            // 調用指定的方法
            if (method.getName().equals("testString")) {
                Object returnString = method.invoke(p, new String("hello world"));
                System.out.println(returnString);
            }
        }
        // 獲取類中註解的方法
        Study annotation = aClass1.getAnnotation(Study.class);
        String value = annotation.value();
        System.out.println(value);
}

 三、反射和註解組合使用

通過反射加註解我們來實現自定義sql的編寫:代碼示例如下

定義我們對應的兩個註解:

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Column {
    
    String value() default "";
}

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

    String name();
}

在用來定一個Person對象,對應字段和註解使用位置如下:

@Table(name = "person")
public class Person {
    
    private String name;

    private int age;
    
    @Column(value = "id_card")
    private String idCard;
    // 省略get/set方法
}

定一個生成sql的util

public class SqlUtil {
    
    public static String createSql(Object o) throws IllegalAccessException {
        Class<?> aClass = o.getClass();
        StringBuffer sb = new StringBuffer();
        StringBuffer whereSql = new StringBuffer();
        sb.append("select ");
        Field[] declaredFields = aClass.getDeclaredFields();
        for (Field field : declaredFields) {
            field.setAccessible(true);
            Column column = field.getAnnotation(Column.class);
            String name = field.getName();
            if (column != null) {
                String value = column.value();
                sb.append(value + ",");
            } else {
                sb.append(name + ",");
            }
            Object o1 = field.get(o);
            if (o1 instanceof String) {
                if (Objects.nonNull(o1)) {
                    whereSql.append("and ").append(name).append("=").append(o1);
                }
            } else if (o1 instanceof Integer) {
                if ((Integer)o1 != 0) {
                    whereSql.append("and ").append(name).append("=").append(o1);
                }
            }

        }
        sb.deleteCharAt(sb.length() - 1);
        sb.append(" from ");
        Table annotation = aClass.getAnnotation(Table.class);
        String name = annotation.name();
        sb.append(name).append(" where 1=1 ");
        sb.append(whereSql);
        return sb.toString();

    }

}

 自定義測試類

public static void main(String[] args) throws Exception {

    Person person = new Person();
    person.setName("hdd");
    String sql = SqlUtil.createSql(person);
    System.out.println(sql);
}

輸出結果爲:

select name,age,id_card from person where 1=1 and name=hdd

 

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