欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

自定義注解基本概念和使用方式

 更新時間:2024年08月21日 15:02:18   作者:?abc!  
這篇文章主要介紹了自定義注解基本概念和使用方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教

1. 概念

1.1 元注解

元注解的作用就是負責(zé)注解其他注解。Java5.0定義了4個標(biāo)準(zhǔn)的meta-annotation類型,它們被用來提供對其它 annotation類型作說明

Java5.0定義的元注解:java.lang.annotation包

  • @Target:描述了注解修飾的對象范圍
  • @Retention:表示注解保留時間長短
  • @Documented:表示是否將此注解的相關(guān)信息添加到j(luò)avadoc文檔中
  • @Inherited是否允許子類繼承該注解,只有在類上使用時才會有效,對方法,屬性等其他無效

1.1.1 Target

描述了注解修飾的對象范圍

取值在java.lang.annotation.ElementType定義,常用的包括:

  • METHOD:用于描述方法
  • PACKAGE:用于描述包
  • PARAMETER:用于描述方法變量
  • TYPE:用于描述類、接口或enum類型
  • CONSTRUCTOR:用于描述構(gòu)造器
  • FIELD:用于描述域
  • LOCAL_VARIABLE:用于描述局部變量
  • TYPE_PARAMETER:類型參數(shù),表示這個注解可以用在 Type的聲明式前,jdk1.8引入
  • TYPE_USE:類型的注解,表示這個注解可以用在所有使用Type的地方(如:泛型,類型轉(zhuǎn)換等),jdk1.8引入

ElementType 源碼:

public enum ElementType {
    /** Class, interface (including annotation type), or enum declaration */
    TYPE,

    /** Field declaration (includes enum constants) */
    FIELD,

    /** Method declaration */
    METHOD,

    /** Formal parameter declaration */
    PARAMETER,

    /** Constructor declaration */
    CONSTRUCTOR,

    /** Local variable declaration */
    LOCAL_VARIABLE,

    /** Annotation type declaration */
    ANNOTATION_TYPE,

    /** Package declaration */
    PACKAGE,

    /**
     * Type parameter declaration
     *
     * @since 1.8
     */
    TYPE_PARAMETER,

    /**
     * Use of a type
     *
     * @since 1.8
     */
    TYPE_USE
}

1.1.1.1 示例

注解Table 可以用于注解類、接口(包括注解類型) 或enum聲明

注解NoDBColumn僅可用于注解類的成員變量。

@Target(ElementType.TYPE)
public @interface Table {
    /**
     * 數(shù)據(jù)表名稱注解,默認值為類名稱
     * @return
     */
    public String tableName() default "className";
}

@Target(ElementType.FIELD)
public @interface NoDBColumn {

}

1.1.2 Retention

表示注解保留時間長短

取值在java.lang.annotation.RetentionPolicy中,取值為:

  • SOURCE:在源文件中有效,編譯過程中會被忽略
  • CLASS:隨源文件一起編譯在class文件中,運行時忽略
  • RUNTIME:在運行時有效

只有定義為RetentionPolicy.RUNTIME時,我們才能通過注解反射獲取到注解。

1.1.2.1 示例

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Column {
    public String name() default "fieldName";
    public String setFuncName() default "setField";
    public String getFuncName() default "getField"; 
    public boolean defaultDBValue() default false;
}

1.1.3 Documented

描述其它類型的annotation應(yīng)該被作為被標(biāo)注的程序成員的公共API,因此可以被例如javadoc此類的工具文檔化。

Documented是一個標(biāo)記注解,沒有成員

表示是否將此注解的相關(guān)信息添加到j(luò)avadoc文檔中

1.1.4 Inherited

定義該注解和子類的關(guān)系,使用此注解聲明出來的自定義注解,在使用在類上面時,子類會自動繼承此注解,否則,子類不會繼承此注解。

注意:

  • 使用Inherited聲明出來的注解,只有在類上使用時才會有效,對方法,屬性等其他無效。
  • @Inherited annotation類型是被標(biāo)注過的class的子類所繼承。類并不從它所實現(xiàn)的接口繼承annotation,方法并不從它所重載的方法繼承annotation。
  • 類型標(biāo)注的annotation的Retention是RetentionPolicy.RUNTIME,如果我們使用java.lang.reflect去查詢一個@Inherited annotation類型的annotation時,反射代碼檢查將展開工作:檢查class和其父類,直到發(fā)現(xiàn)指定的annotation類型被發(fā)現(xiàn),或者到達類繼承結(jié)構(gòu)的頂層。

1.1.4.1 示例

@Inherited
public @interface Greeting {
    public enum FontColor{ BULE,RED,GREEN};
    String name();
    FontColor fontColor() default FontColor.GREEN;
}

1.2 自定義注解

@interface用來聲明一個注解,其中的每一個方法實際上是聲明了一個配置參數(shù)

  • 方法的名稱就是參數(shù)的名稱,
  • 返回值類型就是參數(shù)的類型(返回值類型只能是基本類型、Class、String、enum)
  • 可以通過default來聲明參數(shù)的默認值。

1.2.1 使用格式

public @interface 注解名 {定義體}

1.2.2 支持數(shù)據(jù)類型

注解參數(shù)可支持數(shù)據(jù)類型:

  • 所有基本數(shù)據(jù)類型(int,float,boolean,byte,double,char,long,short)
  • String類型
  • Class類型
  • enum類型
  • Annotation類型
  • 以上所有類型的數(shù)組

Annotation類型里面的參數(shù)該怎么設(shè)定:

  • 只能用public或默認(default)這兩個訪問權(quán)修飾.例如,String value();這里把方法設(shè)為defaul默認類型、
  • 參數(shù)成員只能用基本類型byte,short,char,int,long,float,double,boolean八種基本數(shù)據(jù)類型和 String,Enum,Class,annotations等數(shù)據(jù)類型,以及這一些類型的數(shù)組

例如,String value();這里的參數(shù)成員就為String;

如果只有一個參數(shù)成員,最好把參數(shù)名稱設(shè)為"value",后加小括號.

例:下面的例子FruitName注解就只有一個參數(shù)成員。

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

1.2.3 注解元素的默認值

注解元素必須有確定的值,要么在定義注解的默認值中指定,要么在使用注解時指定,非基本類型的注解元素的值不可為null

  • 使用空字符串或0作為默認值是一種常用的做法
  • 這個約束使得處理器很難表現(xiàn)一個元素的存在或缺失的狀態(tài),因為每個注解的聲明中,所有元素都存在,并且都具有相應(yīng)的值,為了繞開這個約束,我們只能定義一些特殊的值,例如空字符串或者負數(shù),一次表示某個元素不存在,在定義注解時,這已經(jīng)成為一個習(xí)慣用法。

1.3 為什么要使用自定義注解

語義清晰:自定義注解可以使代碼的意圖更加明確和可讀。

例如,使用 @Transactional 注解可以清晰地表明某個方法需要事務(wù)支持,而不需要查看AOP配置或切面代碼。

  • 簡化配置:可以簡化配置,減少樣板代碼。通過注解,開發(fā)者可以直接在代碼中聲明需要的行為,而不需要在外部配置文件中進行復(fù)雜的配置
  • 增強可維護性:注解使得代碼更加模塊化和可維護。開發(fā)者可以通過注解快速定位和理解代碼的行為,而不需要深入理解AOP的實現(xiàn)細節(jié)
  • 靈活性:自定義注解可以與AOP結(jié)合使用,提供更靈活的解決方案。例如,可以定義多個注解來表示不同的切面邏輯,然后在切面中根據(jù)注解類型進行不同的處理。

2. 使用注意

2.1 不生效情況

保留策略不正確:注解可能在運行時不可見。

  • 解決方法:確保注解的保留策略設(shè)置為 RetentionPolicy.RUNTIME,這樣注解在運行時可通過反射獲取。

目標(biāo)元素不正確:目標(biāo)元素(Target Element)設(shè)置不正確,注解可能無法應(yīng)用到期望的程序元素上。

  • 解決方法:確保注解的目標(biāo)元素設(shè)置正確,例如 ElementType.METHOD、ElementType.FIELD 等

未啟用AOP:如果使用AOP來處理注解,但未啟用AOP支持,注解處理邏輯將不會生效

解決方法:確保在Spring Boot應(yīng)用的主類上添加 @EnableAspectJAutoProxy 注解。

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@SpringBootApplication
@EnableAspectJAutoProxy
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

Spring Boot的自動配置機制會根據(jù)類路徑中的依賴和配置文件中的屬性自動配置許多常見的功能。

例如,spring-boot-starter-aop 依賴會自動啟用AOP支持

切面未被Spring管理:如果切面類未被Spring管理,AOP切面將不會生效。

  • 解決方法:確保切面類上添加了 @Component 注解,或者通過其他方式將其注冊為Spring Bean。
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class MyAspect {
    // 切面邏輯
}

注解處理邏輯有誤:如果注解處理邏輯有誤,注解可能不會按預(yù)期生效。

  • 解決方法:檢查注解處理邏輯,確保正確處理注解。例如,使用反射獲取注解時,確保方法簽名和注解類型正確。
import java.lang.reflect.Method;

public class Main {
    public static void main(String[] args) throws Exception {
        Method method = MyClass.class.getMethod("myMethod");
        MyAnnotation annotation = method.getAnnotation(MyAnnotation.class);
        if (annotation != null) {
            // 處理注解
        }
    }
}

注解未正確應(yīng)用:如果注解未正確應(yīng)用到目標(biāo)元素上,注解將不會生效。

  • 解決方法:確保注解正確應(yīng)用到目標(biāo)元素上,例如方法、字段、類等。
public class MyClass {
    @MyAnnotation
    public void myMethod() {
        // 方法實現(xiàn)
    }
}

2.2 其他

自定義注解可以在Java和Spring項目中使用。具體來說:

  • Java:Java本身提供了注解的功能,允許開發(fā)者定義和使用自定義注解。自定義注解可以用于代碼文檔、編譯時檢查、運行時行為等。
  • Spring:Spring框架廣泛使用注解來配置和管理Bean、事務(wù)、AOP等。你可以在Spring項目中定義自定義注解,并結(jié)合Spring的功能(如AOP、依賴注入等)來實現(xiàn)特定的業(yè)務(wù)邏輯。

因此,自定義注解既可以用于純Java項目,也可以用于Spring項目。具體取決于你的需求和項目類型。

3. 實例

3.1 自定義注解 實現(xiàn)賦值和校驗

定義兩個注解,一個用來賦值,一個用來校驗。

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

    enum SEX_TYPE {MAN, WOMAN}

    SEX_TYPE sex() default SEX_TYPE.MAN;
}
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD,ElementType.METHOD})
@Inherited
public @interface ValidateAge {

    /**
     * 最小值
     */
    int min() default 18;

    /**
     * 最大值
     */
    int max() default 99;

    /**
     * 默認值
     */
    int value() default 20;
}

定義User類

@Data
public class User {

    private String username;

    @ValidateAge(min = 20, max = 35, value = 22)
    private int age;

    @InitSex(sex = InitSex.SEX_TYPE.MAN)
    private String sex;
}

測試調(diào)用

public static void main(String[] args) throws IllegalAccessException {
    User user = new User();
    initUser(user);
    boolean checkResult = checkUser(user);
    printResult(checkResult);
}

static boolean checkUser(User user) throws IllegalAccessException {

        // 獲取User類中所有的屬性(getFields無法獲得private屬性)
        Field[] fields = User.class.getDeclaredFields();

        boolean result = true;
        // 遍歷所有屬性
        for (Field field : fields) {
                // 如果屬性上有此注解,則進行賦值操作
                if (field.isAnnotationPresent(ValidateAge.class)) {
                        ValidateAge validateAge = field.getAnnotation(ValidateAge.class);
                        field.setAccessible(true);
                        int age = (int) field.get(user);
                        if (age < validateAge.min() || age > validateAge.max()) {
                                result = false;
                                System.out.println("年齡值不符合條件");
                        }
                }
        }

        return result;
}
static void initUser(User user) throws IllegalAccessException {

        // 獲取User類中所有的屬性(getFields無法獲得private屬性)
        Field[] fields = User.class.getDeclaredFields();

        // 遍歷所有屬性
        for (Field field : fields) {
                // 如果屬性上有此注解,則進行賦值操作
                if (field.isAnnotationPresent(InitSex.class)) {
                        InitSex init = field.getAnnotation(InitSex.class);
                        field.setAccessible(true);
                        // 設(shè)置屬性的性別值
                        field.set(user, init.sex().toString());
                        System.out.println("完成屬性值的修改,修改值為:" + init.sex().toString());
                }
        }
}

3.2 自定義注解+攔截器 實現(xiàn)登錄校驗

如果方法上加了@LoginRequired,則提示用戶該接口需要登錄才能訪問,否則不需要登錄。

定義自定義注解:LoginRequired

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LoginRequired {
    
}

定義兩個簡單的接口

@RestController
public class testController {    
    @GetMapping("/sourceA")    
    public String sourceA(){
            return "你正在訪問sourceA資源";    
    }
    
    @LoginRequired    
    @GetMapping("/sourceB")    
    public String sourceB(){
            return "你正在訪問sourceB資源";    
    }
}

實現(xiàn)spring的HandlerInterceptor 類,重寫preHandle實現(xiàn)攔截器,登錄攔截邏輯

@Slf4j
public class SourceAccessInterceptor implements HandlerInterceptor {
    @Override    
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        log.info("進入攔截器了"); 
        // 反射獲取方法上的LoginRequred注解        
        HandlerMethod handlerMethod = (HandlerMethod)handler;        
        LoginRequired loginRequired = handlerMethod.getMethod().getAnnotation(LoginRequired.class);        
        if(loginRequired == null){
            return true;        
        }        
        // 有LoginRequired注解說明需要登錄,提示用戶登錄        
        response.setContentType("application/json; charset=utf-8");        
        response.getWriter().print("你訪問的資源需要登錄");        
        return false; 
    }    
    @Override    
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {    }    
    @Override    
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {    }}

實現(xiàn)spring類WebMvcConfigurer,創(chuàng)建配置類把攔截器添加到攔截器鏈中

@Configuration
public class InterceptorTrainConfigurer implements WebMvcConfigurer {
    @Override    
    public void addInterceptors(InterceptorRegistry registry) {
            registry.addInterceptor(new SourceAccessInterceptor()).addPathPatterns("/**");    
    }
}

3.3 自定義注解+AOP 實現(xiàn)日志打印

切面需要的依賴包

<dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

自定義注解@MyLog

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyLog{
    
}

定義切面類

@Aspect // 1.表明這是一個切面類
@Component
public class MyLogAspect {

    // 2. PointCut表示這是一個切點,@annotation表示這個切點切到一個注解上,后面帶該注解的全類名
    // 切面最主要的就是切點,所有的故事都圍繞切點發(fā)生
    // logPointCut()代表切點名稱
    @Pointcut("@annotation(me.zebin.demo.annotationdemo.aoplog.MyLog)")
    public void logPointCut(){};

    // 3. 環(huán)繞通知
    @Around("logPointCut()")
    public void logAround(ProceedingJoinPoint joinPoint){
        // 獲取方法名稱
        String methodName = joinPoint.getSignature().getName();
        // 獲取入?yún)?
        Object[] param = joinPoint.getArgs();

        StringBuilder sb = new StringBuilder();
        for(Object o : param){
            sb.append(o + "; ");
        }
        System.out.println("進入[" + methodName + "]方法,參數(shù)為:" + sb.toString());

        // 繼續(xù)執(zhí)行方法
        try {
            joinPoint.proceed();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        System.out.println(methodName + "方法執(zhí)行結(jié)束");

    }
}

使用

@MyLog
@GetMapping("/sourceC/{source_name}")
public String sourceC(@PathVariable("source_name") String sourceName){
    return "你正在訪問sourceC資源";
}

總結(jié)

以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • MyBatis實現(xiàn)三級樹查詢的示例代碼

    MyBatis實現(xiàn)三級樹查詢的示例代碼

    在實際項目開發(fā)中,樹形結(jié)構(gòu)的數(shù)據(jù)查詢是一個非常常見的需求,比如組織架構(gòu)、菜單管理、地區(qū)選擇等場景都需要處理樹形數(shù)據(jù),本文將詳細講解如何使用MyBatis實現(xiàn)三級樹形數(shù)據(jù)的查詢,需要的朋友可以參考下
    2024-12-12
  • JAVA 枚舉相關(guān)知識匯總

    JAVA 枚舉相關(guān)知識匯總

    這篇文章主要介紹了JAVA 枚舉相關(guān)知識,文中講解的非常詳細,代碼幫助大家更好的參考和學(xué)習(xí),感興趣的朋友可以了解下
    2020-06-06
  • 初學(xué)者易上手的SSH-struts2 01環(huán)境搭建(圖文教程)

    初學(xué)者易上手的SSH-struts2 01環(huán)境搭建(圖文教程)

    下面小編就為大家?guī)硪黄鯇W(xué)者易上手的SSH-struts2 01環(huán)境搭建(圖文教程)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-10-10
  • SpringBoot獲取當(dāng)前運行環(huán)境三種方式小結(jié)

    SpringBoot獲取當(dāng)前運行環(huán)境三種方式小結(jié)

    在使用SpringBoot過程中,我們只需要引入相關(guān)依賴,然后在main方法中調(diào)用SpringBootApplication.run(應(yīng)用程序啟動類.class)方法即可,那么SpringBoot是如何獲取當(dāng)前運行環(huán)境呢,接下來由小編給大家介紹一下SpringBoot獲取當(dāng)前運行環(huán)境三種方式,需要的朋友可以參考下
    2024-01-01
  • Javaweb項目session超時解決方案

    Javaweb項目session超時解決方案

    這篇文章主要介紹了Javaweb項目session超時解決方案,關(guān)于解決方案分類比較明確,內(nèi)容詳細,需要的朋友可以參考下。
    2017-09-09
  • SpringBoot整合easy-es的詳細過程

    SpringBoot整合easy-es的詳細過程

    本文介紹了EasyES,一個基于Elasticsearch的ORM框架,旨在簡化開發(fā)流程并提高效率,EasyES支持SpringBoot框架,并提供了CRUD操作、批量操作和查詢操作等方法,文章還列舉了使用EasyES時可能遇到的技術(shù)難題及解決方法,感興趣的朋友一起看看吧
    2025-02-02
  • Java的四種常見線程池及Scheduled定時線程池實現(xiàn)詳解

    Java的四種常見線程池及Scheduled定時線程池實現(xiàn)詳解

    這篇文章主要介紹了Java的四種常見線程池及Scheduled定時線程池實現(xiàn)詳解,在Java中,我們可以通過Executors類來創(chuàng)建ScheduledThreadPool,Executors類提供了幾個靜態(tài)方法來創(chuàng)建不同類型的線程池,包括ScheduledThreadPool,需要的朋友可以參考下
    2023-09-09
  • java設(shè)計模式學(xué)習(xí)之簡單工廠模式

    java設(shè)計模式學(xué)習(xí)之簡單工廠模式

    這篇文章主要為大家詳細介紹了java設(shè)計模式學(xué)習(xí)之簡單工廠模式,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-10-10
  • Java8 HashMap鍵與Comparable接口小結(jié)

    Java8 HashMap鍵與Comparable接口小結(jié)

    這篇文章主要介紹了Java8 HashMap鍵與Comparable接口小結(jié),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-01-01
  • Java springboot yaml語法注解

    Java springboot yaml語法注解

    這篇文章主要介紹了SpringBoot中的yaml語法及靜態(tài)資源訪問問題,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-09-09

最新評論