java中如何自定義註解

做爲一名開發人員,註解的的使用是最多見的了,好比Spring框架裏的業務層註解@Service、@Transaction,控制層用的@Controller、@Autowired,SpringBoot框架的啓動類註解@SpringBootApplication等等。那麼如何自定義註解呢?java

1、什麼是註解

註解(Annotation)是元數據的一種形式,從JDK5.0 引入,它能爲代碼提供一些相關數據,以便於在代碼編譯或運行時使用。spring

2、註解的做用

Java中的註解能夠修飾類、方法、變量、參數等。註解能夠經過反射手段獲取其內容,在編譯器生成類文件時,註解能夠被嵌入到字節碼中。固然JVM也能夠保留註解的內容,在運行時動態,總結起來主要是如下幾個層面:數組

  • 編譯器根據註解在編譯代碼時行進行提示警告或錯誤信息 例如咱們在使用java.util包下的Date類時,調用了類中被@Deprecated標註的方法,IDE會在編譯時會有警告信息
public static void main(String[] args) {
    Date date = new Date();
    //JDK源碼中,Date類的getDay方法被@Deprecated註解標註,表明方法已過期
    int day = date.getDay();
}
複製代碼
  • 編譯運行時時根據註解動態生成代碼 例如在springboot框架中,咱們實現一個Controller層方法的前置通知,經過使用@Aspect、@Before等註解便可,固然這些註解是框架封裝好的,屏蔽了底層的細節,可是AOP的原理,你們應該都很熟悉
@Slf4j
@Component
@Aspect
public class MyAspect {
     
    @Before(value = "execution(public * com.test.controller.*.*(..))")
    public void before(JoinPoint joinPoint) {
        log.info("CLASS_METHOD:[{}]" , joinPoint.getSignature().getName());
    }
}
複製代碼
  • 程序運行時使用註解進行動態賦值 好比經過使用@Value註解,將配置文件參數賦給代碼裏面變量,例如SpringBoot裏面集成RabbitMq時,帳戶密碼等配置信息經過註解進行配置
@Configuration
public class RabbitMqConfig {

    @Value("${spring.rabbitmq.host}")
    private String rabbitMqHost;

    @Value("${spring.rabbitmq.port}")
    private String rabbitMqPort;
}
複製代碼

3、自定義註解

首先咱們先看下一個註解示例,下面是javafx.beans包下的@DefaultProperty註解 :springboot

package javafx.beans;

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

/** * Specifies a property to which child elements will be added or set when an * explicit property is not given. * * @since JavaFX 2.0 */
@Inherited
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface DefaultProperty {
    /** * The name of the default property. */
    public String value();
}
複製代碼

咱們看到定義註解與定義一個class類類似,不過其中class關鍵字被替換爲了@interface,註解裏聲明瞭一個String類型的value屬性,注意這種聲明屬性方式!稍後會詳細說明。同時還能夠看到DefaultProperty註解被@Inherited、@Documented、@Retention(RetentionPolicy.RUNTIME)、@Target(ElementType.TYPE)這些註解所修飾,這就是咱們須要知道另一個概念——元註解(meta-annotations)。bash

1.元註解

元註解是咱們在定義註解時須要用到的一些特殊含義的註解,能夠說它們是對聲明註解時的註解。java語言爲咱們默認提供瞭如下元註解,在java.lang.annotation包下,咱們來看下:框架

  • @Retention(RetentionPolicy.XX) 註解的保留域,表示註解的保留範圍,可選項有ide

    • RetentionPolicy.SOURCE – 源代碼級別保留,編譯器編譯後該類型的註解就被丟棄掉了,生成的.class字節碼文件中,將再也不存在該類型的註解.
    • RetentionPolicy.CLASS – .class字節碼文件中保留,編譯器編譯後保留,JVM加載後丟棄掉,運行時沒法獲取
    • RetentionPolicy.RUNTIME – 運行時保留,在運行時,JVM使用反射,能夠獲取註解屬性內容,絕大多數註解在定義是使用都是該級別
  • @Target( ElementType.XX) 指定該註解可使用的地方,如類聲明、方法聲明,變量聲明等等, 在定義註解時,若是沒有使用Target指定,默認均可以使用。若是使用了Target指定使用的位置,那麼該註解只能在所指定的位置使用工具

    • ElementType.ANNOTATION_TYPE 註解類型聲明
    • ElementType.CONSTRUCTOR 構造方法
    • ElementType.FIELD 字段聲明(包括枚舉常量)
    • ElementType.LOCAL_VARIABLE 局部變量聲明
    • ElementType.METHOD 方法聲明
    • ElementType.PACKAGE 包聲明
    • ElementType.PARAMETER 方法的參數聲明
    • ElementType.TYPE 類、接口(包括註解類型)或enum聲明
  • @Documented 表示在使用Javadoc工具生成文檔時,包含此註解信息spa

  • @Inherited 表示當前註解是可繼承的,父類中所使用的註解若是被@Inherited修飾,子類會繼承父類中對應的註解code

2.註解屬性定義方式

明白了註解的外在定義形式,那麼咱們就來看下註解內部的屬性的定義方式,Talk is cheap. Show me the code 多說無益,直接上代碼

@Documented
@Inherited
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    /** *一、屬性的定義和接口中方法聲明相似,訪問修飾符默認是public,可省略,注意屬性名後面跟了() */
    String name();
    
    /** * 二、能夠經過default爲屬性指定默認值,在註解使用時,能夠爲之賦值, 也能夠不賦值 * 固然若是不經過default爲屬性指定默認值,在註解使用必須使用該屬性而且爲之賦值 */
    int age() default 1;
    
    /** * 三、註解中的屬性value比較特殊,若是使用註解時僅爲該屬性賦值,"value="能夠省略掉, * 可是若是和其餘屬性同時賦值,「value=」則不能省略,這個特性和value的屬性類型無關 */
    String value() default "";
   
    /** * 4.註解中屬性的類型能夠是基本數據類型及其數組、類、枚舉、註解 */
    boolean sex() default true;
    
}

複製代碼

4、經過反射獲取註解屬性值

在上面代碼中,咱們自定義了@MyAnnotation註解,而且指明@Target({ElementType.METHOD})代表此註解只能用在方法聲明上, @Retention(RetentionPolicy.RUNTIME)指定其保留到代碼運行時,因此咱們能夠經過反射獲取MyAnnotation的屬性值。下面咱們定義了一個Person類,並在其中定義了一個sayHello方法,在方法聲明上,咱們使用@MyAnnotation註解,咱們將使用反射調用sayHello方法,而且使用MyAnnotation註解中的屬性值

package com.test.annotation;
import com.test.enu.Color;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class Person {

    @MyAnnotation(name="韓梅梅",age = 30,sex = true,clothes = Color.YELLOW)
    public void sayHello(String name){
        System.out.println("Hello!" + name);
    }

    public static void main(String[] args) throws InvocationTargetException, IllegalAccessException {
        Person person = new Person();
        //獲取Person類的Class對象
        Class<? extends Person> personClass = person.getClass();
        //獲取類中聲明的方法列表
        Method[] declaredMethods = personClass.getDeclaredMethods();
        for (Method method : declaredMethods) {
            //判斷當前方法是否含有MyAnnotation註解
            if(method.isAnnotationPresent(MyAnnotation.class)){
                //獲取MyAnnotation類型註解
                MyAnnotation myAnnotation = method.getAnnotation(MyAnnotation.class);
                //反射調用方法,並傳遞註解name屬性值爲參數
                Object invoke = method.invoke(person,myAnnotation.name());
                //打印註解中定義的各個類型的值
                System.out.println(myAnnotation);
                System.out.println(myAnnotation.name()+","+myAnnotation.sex()+","+myAnnotation.age());
            }
        }
    }
}

複製代碼

此時的IDE控制檯輸出,說明咱們經過反射在運行時獲取到了@MyAnnotation註解的值

Hello!韓梅梅
韓梅梅,true,30
@com.test.annotation.MyAnnotation(value=, age=30, sex=true, name=韓梅梅, clothes=YELLOW)
複製代碼

5、JDK內部自帶註解

JDK自帶了一些原先定義好的註解,咱們能夠直接使用

  • @Override 表示當前方法覆蓋了父類的方法
  • @Deprecation 表示方法已通過時,方法上有橫線,使用時會有警告。
  • @SuppviseWarnings 表示關閉一些警告信息(通知java編譯器忽略特定的編譯警告)

至於註解內部詳細內容,你們能夠點進去源碼查看。但願本篇文章對你有所幫助!

相關文章
相關標籤/搜索