java自定義註解實現過程,java自定義註解實現過程圖

本文目錄一覽:

如何實現自定義Java運行時註解功能

相信大家在Android開發過程中,對註解有一定程度的接觸,比如我們在使用EventBus的時候,我們聲明

@Subscribe(threadMode = ThreadMode.MainThread)

來接受通過EventBus發出來的數據,

還有ButterKnife,我們在使用OnClick添加點擊事件或Bind綁定組件的時候,同樣也有類似的寫法

@Bind(R.id.index_image)

@OnClick({R.id.index_layout)

以至於什麼@Override、@Deprecated、@SuppressWarnings這就不需要說了。

我們在寫的時候,最大的感觸就是:太方便了!!!直接加一個聲明就Ok了。你是不是心動了,自己也寫幾個註解,是不是程序能夠更加簡潔一些了?先別急,看看註解的結構

@Retention(CLASS)

@Target(FIELD)

public @interface Bind {

/** View ID to which the field will be bound. */

int[] value();

}

其實一點概念都不知道的話,確實看到這個會懵逼。我們一個個來解釋一下

Retention 表示在什麼級別保留此信息

SOURCE:源碼註解,註解僅存在代碼中,註解會被編譯器丟棄

CLASS:編譯時註解,註解會在class文件中保留,但會被VM丟棄

RUNTIME:運行時註解,VM運行期間也會保留該註解,因此可以通過反射來獲得該註解

Target 表示作用域,可能的ElementType參數包括:

CONSTRUCTOR:構造方法聲明

FIELD:欄位聲明

LOCAL_VARIABLE:局部變數聲明

METHOD:方法聲明

PACKAGE:包聲明

PARAMETER:參數聲明

TYPE:類,介面或enum聲明

Inherited 容許子類繼承

Documented 生成javadoc時會包含註解

java內部註解是如何實現的

用一個詞就可以描述註解,那就是元數據,即一種描述數據的數據。所以,可以說註解就是源代碼的元數據。比如,下面這段代碼:

@Override

public String toString() {

return “This is String Representation of current object.”;

}

上面的代碼中,我重寫了toString()方法並使用了@Override註解。但是,即使我不使用@Override註解標記代碼,程序也能夠正常執行。那麼,該註解表示什麼?這麼寫有什麼好處嗎?事實上,@Override告訴編譯器這個方法是一個重寫方法(描述方法的元數據),如果父類中不存在該方法,編譯器便會報錯,提示該方法沒有重寫父類中的方法。如果我不小心拼寫錯誤,例如將toString()寫成了toStrring(){double r},而且我也沒有使用@Override註解,那程序依然能編譯運行。但運行結果會和我期望的大不相同。現在我們了解了什麼是註解,並且使用註解有助於閱讀程序。

Annotation是一種應用於類、方法、參數、變數、構造器及包聲明中的特殊修飾符。它是一種由JSR-175標準選擇用來描述元數據的一種工具。

為什麼要引入註解?

使用Annotation之前(甚至在使用之後),XML被廣泛的應用於描述元數據。不知何時開始一些應用開發人員和架構師發現XML的維護越來越糟糕了。他們希望使用一些和代碼緊耦合的東西,而不是像XML那樣和代碼是松耦合的(在某些情況下甚至是完全分離的)代碼描述。如果你在Google中搜索「XML vs. annotations」,會看到許多關於這個問題的辯論。最有趣的是XML配置其實就是為了分離代碼和配置而引入的。上述兩種觀點可能會讓你很疑惑,兩者觀點似乎構成了一種循環,但各有利弊。下面我們通過一個例子來理解這兩者的區別。

假如你想為應用設置很多的常量或參數,這種情況下,XML是一個很好的選擇,因為它不會同特定的代碼相連。如果你想把某個方法聲明為服務,那麼使用Annotation會更好一些,因為這種情況下需要註解和方法緊密耦合起來,開發人員也必須認識到這點。

另一個很重要的因素是Annotation定義了一種標準的描述元數據的方式。在這之前,開發人員通常使用他們自己的方式定義元數據。例如,使用標記interfaces,注釋,transient關鍵字等等。每個程序員按照自己的方式定義元數據,而不像Annotation這種標準的方式。

目前,許多框架將XML和Annotation兩種方式結合使用,平衡兩者之間的利弊。

Annotation是如何工作的?怎麼編寫自定義的Annotation?

在講述這部分之前,建議你首先下載Annotation的示例代碼AnnotationsSample.zip 。下載之後放在你習慣使用的IDE中,這些代碼會幫助你更好的理解Annotation機制。

編寫Annotation非常簡單,可以將Annotation的定義同介面的定義進行比較。我們來看兩個例子:一個是標準的註解@Override,另一個是用戶自定義註解@Todo。

@Target(ElementType.METHOD)

@Retention(RetentionPolicy.SOURCE)

public @interface Override {

}

對於@Override注釋你可能有些疑問,它什麼都沒做,那它是如何檢查在父類中有一個同名的函數呢。當然,不要驚訝,我是逗你玩的。@Override註解的定義不僅僅只有這麼一點代碼。這部分內容很重要,我不得不再次重複:Annotations僅僅是元數據,和業務邏輯無關。理解起來有點困難,但就是這樣。如果Annotations不包含業務邏輯,那麼必須有人來實現這些邏輯。元數據的用戶來做這個事情。Annotations僅僅提供它定義的屬性(類/方法/包/域)的信息。Annotations的用戶(同樣是一些代碼)來讀取這些信息並實現必要的邏輯。

當我們使用Java的標註Annotations(例如@Override)時,JVM就是一個用戶,它在位元組碼層面工作。到這裡,應用開發人員還不能控制也不能使用自定義的註解。因此,我們講解一下如何編寫自定義的Annotations。

我們來逐個講述編寫自定義Annotations的要點。上面的例子中,你看到一些註解應用在註解上。

J2SE5.0版本在 java.lang.annotation提供了四種元註解,專門註解其他的註解:

@Documented –註解是否將包含在JavaDoc中

@Retention –什麼時候使用該註解

@Target? –註解用於什麼地方

@Inherited – 是否允許子類繼承該註解

@Documented–一個簡單的Annotations標記註解,表示是否將註解信息添加在java文檔中。

@Retention– 定義該註解的生命周期。

RetentionPolicy.SOURCE – 在編譯階段丟棄。這些註解在編譯結束之後就不再有任何意義,所以它們不會寫入位元組碼。@Override, @SuppressWarnings都屬於這類註解。

RetentionPolicy.CLASS – 在類載入的時候丟棄。在位元組碼文件的處理中有用。註解默認使用這種方式。

RetentionPolicy.RUNTIME– 始終不會丟棄,運行期也保留該註解,因此可以使用反射機制讀取該註解的信息。我們自定義的註解通常使用這種方式。

@Target – 表示該註解用於什麼地方。如果不明確指出,該註解可以放在任何地方。以下是一些可用的參數。需要說明的是:屬性的註解是兼容的,如果你想給7個屬性都添加註解,僅僅排除一個屬性,那麼你需要在定義target包含所有的屬性。

ElementType.TYPE:用於描述類、介面或enum聲明

ElementType.FIELD:用於描述實例變數

ElementType.METHOD

ElementType.PARAMETER

ElementType.CONSTRUCTOR

ElementType.LOCAL_VARIABLE

ElementType.ANNOTATION_TYPE 另一個注釋

ElementType.PACKAGE 用於記錄java文件的package信息

@Inherited – 定義該注釋和子類的關係

那麼,註解的內部到底是如何定義的呢?Annotations只支持基本類型、String及枚舉類型。注釋中所有的屬性被定義成方法,並允許提供默認值。

@Target(ElementType.METHOD)

@Retention(RetentionPolicy.RUNTIME)

@interface Todo {

public enum Priority {LOW, MEDIUM, HIGH}

public enum Status {STARTED, NOT_STARTED}

String author() default “Yash”;

Priority priority() default Priority.LOW;

Status status() default Status.NOT_STARTED;

}

下面的例子演示了如何使用上面的註解。

@Todo(priority = Todo.Priority.MEDIUM, author = “Yashwant”, status = Todo.Status.STARTED)

public void incompleteMethod1() {

//Some business logic is written

//But it』s not complete yet

}

如果註解中只有一個屬性,可以直接命名為「value」,使用時無需再標明屬性名。

@interface Author{

String value();

}

@Author(“Yashwant”)

public void someMethod() {

}

但目前為止一切看起來都還不錯。我們定義了自己的註解並將其應用在業務邏輯的方法上。現在我們需要寫一個用戶程序調用我們的註解。這裡我們需要使用反射機制。如果你熟悉反射代碼,就會知道反射可以提供類名、方法和實例變數對象。所有這些對象都有getAnnotation()這個方法用來返回註解信息。我們需要把這個對象轉換為我們自定義的注釋(使用 instanceOf()檢查之後),同時也可以調用自定義注釋裡面的方法。看看以下的實例代碼,使用了上面的註解:

Class businessLogicClass = BusinessLogic.class;

for(Method method : businessLogicClass.getMethods()) {

Todo todoAnnotation = (Todo)method.getAnnotation(Todo.class);

if(todoAnnotation != null) {

System.out.println(” Method Name : ” + method.getName());

System.out.println(” Author : ” + todoAnnotation.author());

System.out.println(” Priority : ” + todoAnnotation.priority());

System.out.println(” Status : ” + todoAnnotation.status());

}

如何實現自定義註解

首先,定義一個註解:

package com.guxiang.test;

import java.lang.annotation.Documented;

import java.lang.annotation.ElementType;

import java.lang.annotation.Retention;

import java.lang.annotation.RetentionPolicy;

import java.lang.annotation.Target;

首先自定義一個 myTest註解

/**

 * @author guxiang

 * @date 2016年12月24日 下午10:22:11

 * 自定義的myTest註解

 */

@Documented

@Retention(RetentionPolicy.RUNTIME)

@Target({ElementType.METHOD,ElementType.ANNOTATION_TYPE})

public @interface MyTest {

    long time() default -1;

}

其次,寫一個類用於被myTest註解,實現測試:

package com.guxiang.test;

public class SomeDaoImpl {

    public void save(){

        System.out.println(“保存了數據”);

    }

    public void update(){

        System.out.println(“更新了數據”);

    }

}

寫一個測試類 這個類 引用myTest註解:

package com.guxiang.test;

public class SomeDaoImplTest {

    private SomeDaoImpl dao= new SomeDaoImpl();

    /**

     * 測試添加

     */

    @MyTest

    public void testAdd(){

        dao.save();

    }

    @MyTest

    public void testUpdate(){

        dao.update();

    }

最後,寫一個myTestRunner類使用反射實現註解的功能:

package com.guxiang.test;

import java.lang.reflect.InvocationTargetException;

import java.lang.reflect.Method;

// 反射註解

public class MyTestRunner {

    public static void main(String[] args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, InstantiationException {

        Class clazz = SomeDaoImplTest.class;

        Method[] ms = clazz.getMethods();

        for (Method method : ms) {

            boolean hasMyTest = method.isAnnotationPresent(MyTest.class);

            if (hasMyTest) {

                method.invoke(clazz.newInstance(), null);

            }

        }

    }

}

如何實現自定義Java編譯時註解功能

自定義註解,可以應用到反射中,比如自己寫個小框架。

如實現實體類某些屬性不自動賦值,或者驗證某個對象屬性完整性等

本人自己用過的驗證屬性值完整性:

@Target(ElementType.FIELD)

@Retention(RetentionPolicy.RUNTIME)

public @interface IgnoreProperty {

}

然後實體類中:

public class TarResearch implements Serializable{

@IgnoreProperty

private static final long serialVersionUID = 1L;

@IgnoreProperty

private Integer researchId;

@IgnoreProperty

private TarUser userId;

private String version;

private String grade;

….

}

然後action類中

// 驗證數據完整性

ClassTarResearch userClass = TarResearch .class;

Field[] field = userClass.getDeclaredFields();

for (int i = 0; i field.length; i++) {

if (field[i].getAnnotation(IgnoreProperty.class) != null) {

continue;

}

String fie = field[i].getName().substring(0, 1).toUpperCase()

+ field[i].getName().substring(1);

Method method = userClass.getMethod(“get” + fie);

Object obj = method.invoke(u);

if (obj == null) {

sendResponseMsg(response, “數據錯誤”);

return null;

}

}

原創文章,作者:小藍,如若轉載,請註明出處:https://www.506064.com/zh-tw/n/289135.html

(0)
打賞 微信掃一掃 微信掃一掃 支付寶掃一掃 支付寶掃一掃
小藍的頭像小藍
上一篇 2024-12-24 03:01
下一篇 2024-12-24 03:01

相關推薦

  • java client.getacsresponse 編譯報錯解決方法

    java client.getacsresponse 編譯報錯是Java編程過程中常見的錯誤,常見的原因是代碼的語法錯誤、類庫依賴問題和編譯環境的配置問題。下面將從多個方面進行分析…

    編程 2025-04-29
  • Java JsonPath 效率優化指南

    本篇文章將深入探討Java JsonPath的效率問題,並提供一些優化方案。 一、JsonPath 簡介 JsonPath是一個可用於從JSON數據中獲取信息的庫。它提供了一種DS…

    編程 2025-04-29
  • Java騰訊雲音視頻對接

    本文旨在從多個方面詳細闡述Java騰訊雲音視頻對接,提供完整的代碼示例。 一、騰訊雲音視頻介紹 騰訊雲音視頻服務(Cloud Tencent Real-Time Communica…

    編程 2025-04-29
  • Java Bean載入過程

    Java Bean載入過程涉及到類載入器、反射機制和Java虛擬機的執行過程。在本文中,將從這三個方面詳細闡述Java Bean載入的過程。 一、類載入器 類載入器是Java虛擬機…

    編程 2025-04-29
  • Java Milvus SearchParam withoutFields用法介紹

    本文將詳細介紹Java Milvus SearchParam withoutFields的相關知識和用法。 一、什麼是Java Milvus SearchParam without…

    編程 2025-04-29
  • Java 8中某一周的周一

    Java 8是Java語言中的一個版本,於2014年3月18日發布。本文將從多個方面對Java 8中某一周的周一進行詳細的闡述。 一、數組處理 Java 8新特性之一是Stream…

    編程 2025-04-29
  • Java判斷字元串是否存在多個

    本文將從以下幾個方面詳細闡述如何使用Java判斷一個字元串中是否存在多個指定字元: 一、字元串遍歷 字元串是Java編程中非常重要的一種數據類型。要判斷字元串中是否存在多個指定字元…

    編程 2025-04-29
  • VSCode為什麼無法運行Java

    解答:VSCode無法運行Java是因為默認情況下,VSCode並沒有集成Java運行環境,需要手動添加Java運行環境或安裝相關插件才能實現Java代碼的編寫、調試和運行。 一、…

    編程 2025-04-29
  • Java任務下發回滾系統的設計與實現

    本文將介紹一個Java任務下發回滾系統的設計與實現。該系統可以用於執行複雜的任務,包括可回滾的任務,及時恢復任務失敗前的狀態。系統使用Java語言進行開發,可以支持多種類型的任務。…

    編程 2025-04-29
  • Java 8 Group By 會影響排序嗎?

    是的,Java 8中的Group By會對排序產生影響。本文將從多個方面探討Group By對排序的影響。 一、Group By的概述 Group By是SQL中的一種常見操作,它…

    編程 2025-04-29

發表回復

登錄後才能評論