Spring Boot中自定義注解的創建與使用

🌟 前言

歡迎來到我的技術小宇宙!🌌 這里不僅是我記錄技術點滴的后花園,也是我分享學習心得和項目經驗的樂園。📚 無論你是技術小白還是資深大牛,這里總有一些內容能觸動你的好奇心。🔍

  • 🤖 洛可可白:個人主頁

  • 🔥 個人專欄:?前端技術 ?后端技術

  • 🏠 個人博客:洛可可白博客

  • 🐱 代碼獲取:bestwishes0203

  • 📷 封面壁紙:洛可可白wallpaper

在這里插入圖片描述

Spring Boot中自定義注解的創建與使用

  • Spring Boot中自定義注解的創建與使用
    • 一、什么是自定義注解?
    • 二、創建自定義注解
      • (一)定義注解
      • (二)注解的元注解
      • (三)注解的屬性
    • 三、使用自定義注解
      • (一)在Spring中使用自定義注解
        • 1. 創建自定義注解
        • 2. 創建切面類
        • 3. 使用自定義注解
      • (二)通過反射獲取注解信息
        • 1. 定義注解
        • 2. 使用注解
        • 3. 通過反射獲取注解信息
    • 四、自定義注解的高級用法
      • (一)注解的組合
      • (二)注解的繼承
      • (三)注解的動態處理
    • 五、實際應用場景
      • (一)日志記錄
        • 示例:記錄方法的調用信息
      • (二)權限校驗
        • 示例:定義一個`@RequiresPermission`注解
        • 創建切面類處理權限校驗
        • 使用`@RequiresPermission`注解
      • (三)性能監控
        • 示例:定義一個`@MonitorPerformance`注解
        • 創建切面類處理性能監控
        • 使用`@MonitorPerformance`注解
    • 六、最佳實踐
      • (一)合理使用注解
      • (二)結合AOP使用
      • (三)避免濫用反射
    • 七、總結

Spring Boot中自定義注解的創建與使用

在Spring Boot中,自定義注解是一種強大的工具,它可以幫助我們實現代碼的解耦、增強代碼的可讀性和可維護性。通過自定義注解,我們可以在代碼中添加特定的標記,Spring框架可以識別這些標記并執行相應的邏輯。本文將詳細介紹如何在Spring Boot中創建和使用自定義注解。

一、什么是自定義注解?

注解(Annotation)是Java語言中的一種元數據形式,它為程序元素(如類、方法、字段等)提供了一種附加信息的機制。自定義注解是指開發者根據自己的需求定義的注解。通過自定義注解,可以在代碼中添加特定的標記,Spring框架可以識別這些標記并執行相應的邏輯。

二、創建自定義注解

(一)定義注解

自定義注解的定義需要使用@interface關鍵字。以下是一個簡單的自定義注解@Log的定義:

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Target(ElementType.METHOD) // 指定注解可以使用的范圍,這里是方法
@Retention(RetentionPolicy.RUNTIME) // 指定注解的保留策略,這里是運行時
public @interface Log {String operation() default ""; // 定義注解的屬性,這里是一個字符串類型的屬性
}

(二)注解的元注解

在定義自定義注解時,通常會使用一些元注解來指定注解的特性和行為:

  • @Target:指定注解可以使用的范圍,例如類、方法、字段等。常用的值包括:

    • ElementType.TYPE:可以用于類、接口或枚舉。
    • ElementType.METHOD:可以用于方法。
    • ElementType.FIELD:可以用于字段。
    • ElementType.PARAMETER:可以用于方法參數。
  • @Retention:指定注解的保留策略,即注解在什么階段可用。常用的值包括:

    • RetentionPolicy.SOURCE:注解僅在源代碼階段保留,編譯時會被丟棄。
    • RetentionPolicy.CLASS:注解在編譯階段保留,但在運行時不可用。
    • RetentionPolicy.RUNTIME:注解在運行時保留,可以通過反射獲取。
  • @Documented:表示注解應該被Javadoc工具記錄。

  • @Inherited:表示注解可以被子類繼承。

(三)注解的屬性

注解可以定義屬性,這些屬性可以在注解使用時提供具體的值。屬性的定義類似于接口中的方法定義。例如:

public @interface Log {String operation() default ""; // 字符串類型的屬性boolean enabled() default true; // 布爾類型的屬性
}

三、使用自定義注解

(一)在Spring中使用自定義注解

在Spring框架中,可以通過AOP(面向切面編程)來處理自定義注解。以下是一個完整的示例,展示如何使用自定義注解@Log來記錄方法的調用信息。

1. 創建自定義注解
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Log {String operation() default "";
}
2. 創建切面類

使用@Aspect注解定義一個切面類,攔截帶有@Log注解的方法,并記錄日志:

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;import java.lang.reflect.Method;@Aspect
@Component
public class LogAspect {private static final Logger logger = LoggerFactory.getLogger(LogAspect.class);// 定義切入點,攔截帶有@Log注解的方法@Pointcut("@annotation(Log)")public void logPointCut() {}// 環繞通知,記錄方法的調用信息@Around("logPointCut()")public Object around(ProceedingJoinPoint joinPoint) throws Throwable {long startTime = System.currentTimeMillis();// 獲取方法上的注解MethodSignature signature = (MethodSignature) joinPoint.getSignature();Method method = signature.getMethod();Log logAnnotation = method.getAnnotation(Log.class);// 執行方法Object result = joinPoint.proceed();long timeTaken = System.currentTimeMillis() - startTime;logger.info("Method: {}, Operation: {}, Time Taken: {} ms",method.getName(), logAnnotation.operation(), timeTaken);return result;}
}
3. 使用自定義注解

在需要記錄日志的方法上添加@Log注解:

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
public class UserController {@Log(operation = "查詢用戶信息")@GetMapping("/user")public String getUser() {return "User Data";}
}

(二)通過反射獲取注解信息

除了在Spring框架中使用AOP處理注解外,還可以通過反射機制直接獲取注解信息。以下是一個示例:

1. 定義注解
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Log {String operation() default "";
}
2. 使用注解
public class UserService {@Log(operation = "查詢用戶信息")public String getUser() {return "User Data";}
}
3. 通過反射獲取注解信息
import java.lang.reflect.Method;public class AnnotationProcessor {public static void main(String[] args) throws NoSuchMethodException {Method method = UserService.class.getMethod("getUser");if (method.isAnnotationPresent(Log.class)) {Log logAnnotation = method.getAnnotation(Log.class);System.out.println("Operation: " + logAnnotation.operation());}}
}

四、自定義注解的高級用法

(一)注解的組合

可以定義一個注解組合多個注解。例如,定義一個@Loggable注解,組合了@Log@Transactional

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.transaction.annotation.Transactional;@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Transactional
public @interface Loggable {String operation() default "";
}

然后在方法上使用@Loggable注解:

@Loggable(operation = "查詢用戶信息")
public String getUser() {return "User Data";
}

(二)注解的繼承

雖然注解本身不能直接繼承,但可以通過組合注解來實現類似的效果。例如,定義一個@BaseLog注解,然后在其他注解中使用它:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface BaseLog {String operation() default "";
}@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@BaseLog
public @interface Log {String operation() default "";
}

(三)注解的動態處理

在Spring框架中,可以通過@Bean注解和BeanPostProcessor接口動態處理注解。例如,定義一個@MyBean注解,并通過BeanPostProcessor動態注冊Bean:

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;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 MyBean {
}@Configuration
public class MyBeanConfig {@Beanpublic BeanPostProcessor myBeanPostProcessor() {return new BeanPostProcessor() {@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {return bean;}@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {Class<?> beanClass = bean.getClass();if (beanClass.isAnnotationPresent(MyBean.class)) {System.out.println("Bean " + beanName + " is annotated with @MyBean");}return bean;}};}
}@MyBean
public class MyService {public void doSomething() {System.out.println("Doing something...");}
}

五、實際應用場景

(一)日志記錄

日志記錄是自定義注解最常見的應用場景之一。通過自定義注解,我們可以方便地在方法調用前后插入日志邏輯,而無需在每個方法中手動編寫日志代碼。這不僅減少了代碼冗余,還提高了代碼的可維護性。

示例:記錄方法的調用信息

在前面的示例中,我們已經展示了如何通過@Log注解記錄方法的調用信息。這種方法特別適用于需要對系統操作進行審計的場景,例如記錄用戶的操作行為、方法的執行時間等。

(二)權限校驗

在許多應用程序中,某些方法可能需要特定的權限才能執行。通過自定義注解,我們可以方便地實現權限校驗邏輯,而無需在每個方法中手動編寫權限校驗代碼。

示例:定義一個@RequiresPermission注解
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RequiresPermission {String value();
}
創建切面類處理權限校驗
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;@Aspect
@Component
public class PermissionAspect {@Pointcut("@annotation(RequiresPermission)")public void permissionPointCut() {}@Before("permissionPointCut()")public void checkPermission(JoinPoint joinPoint) {MethodSignature signature = (MethodSignature) joinPoint.getSignature();Method method = signature.getMethod();RequiresPermission annotation = method.getAnnotation(RequiresPermission.class);String requiredPermission = annotation.value();// 模擬權限校驗邏輯if (!hasPermission(requiredPermission)) {throw new SecurityException("Access denied: " + requiredPermission);}}private boolean hasPermission(String permission) {// 實際應用中,這里可以調用權限服務進行校驗return true; // 示例中直接返回true}
}
使用@RequiresPermission注解
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
public class AdminController {@RequiresPermission("ADMIN_ACCESS")@GetMapping("/admin/data")public String getAdminData() {return "Admin Data";}
}

(三)性能監控

在一些對性能要求較高的系統中,我們可能需要監控某些方法的執行時間。通過自定義注解,我們可以方便地實現性能監控邏輯,而無需在每個方法中手動編寫性能監控代碼。

示例:定義一個@MonitorPerformance注解
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MonitorPerformance {
}
創建切面類處理性能監控
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;@Aspect
@Component
public class PerformanceAspect {@Pointcut("@annotation(MonitorPerformance)")public void performancePointCut() {}@Around("performancePointCut()")public Object monitorPerformance(ProceedingJoinPoint joinPoint) throws Throwable {long startTime = System.currentTimeMillis();Object result = joinPoint.proceed();long timeTaken = System.currentTimeMillis() - startTime;System.out.println("Method: " + joinPoint.getSignature().getName() + " took " + timeTaken + " ms");return result;}
}
使用@MonitorPerformance注解
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
public class DataService {@MonitorPerformance@GetMapping("/data")public String getData() {// 模擬耗時操作Thread.sleep(1000);return "Data";}
}

六、最佳實踐

(一)合理使用注解

雖然自定義注解非常強大,但過度使用可能會導致代碼難以理解和維護。因此,在使用自定義注解時,應遵循以下原則:

  • 明確注解的用途:每個注解應該有明確的用途,避免注解的功能過于復雜。
  • 保持注解的簡潔性:注解的定義應該盡量簡潔,避免過多的屬性。
  • 合理使用元注解:根據注解的用途,合理選擇@Target@Retention等元注解。

(二)結合AOP使用

在Spring框架中,自定義注解通常與AOP結合使用。通過AOP,我們可以在不修改業務邏輯代碼的情況下,插入額外的邏輯(如日志記錄、權限校驗等)。這種解耦的方式不僅提高了代碼的可維護性,還增強了代碼的可擴展性。

(三)避免濫用反射

雖然反射可以動態獲取注解信息,但反射的性能開銷較大,且代碼可讀性較差。因此,在使用反射時,應盡量避免濫用。在Spring框架中,優先使用AOP來處理注解邏輯。

七、總結

自定義注解是Java和Spring框架中一個非常強大的功能,它可以幫助我們實現代碼的解耦、增強代碼的可讀性和可維護性。通過定義自定義注解,我們可以在代碼中添加特定的標記,Spring框架可以識別這些標記并執行相應的邏輯。本文通過多個實際應用場景,展示了如何在Spring Boot中創建和使用自定義注解。

希望本文對你理解和使用自定義注解有所幫助。如果你有任何問題或建議,歡迎在評論區留言,我們一起交流學習!

如果對你有幫助,點贊👍、收藏💖、關注🔔是我更新的動力!👋🌟🚀

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/75632.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/75632.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/75632.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

2024第十五屆藍橋杯大賽軟件賽省賽C/C++ 大學 B 組

記錄刷題的過程、感悟、題解。 希望能幫到&#xff0c;那些與我一同前行的&#xff0c;來自遠方的朋友&#x1f609; 大綱&#xff1a; 1、握手問題-&#xff08;解析&#xff09;-簡單組合問題&#xff08;別人叫她 鴿巢定理&#xff09;&#x1f607;&#xff0c;感覺叫高級了…

HTML應用指南:利用POST請求獲取三大運營商5G基站位置信息(一)

在當前信息技術迅猛發展的背景下,第五代移動通信(5G)技術作為新一代的無線通信標準,正逐步成為推動社會進步和產業升級的關鍵驅動力。三大電信運營商(中國移動、中國聯通、中國電信)在全國范圍內的5G基站部署,不僅極大地提升了網絡性能,也為智能城市、物聯網、自動駕駛…

C++學習之線程

目錄 1.進程和線程的概念 2.線程內核三級映射 3.線程優缺點 4.創建線程和獲取線程ID的函數 5.創建子線程 6.循環創建N個子線程 7.子線程傳參地址錯誤演示分析 8.主、子線程共享全局變量、堆空間 9.線程退出 10.pthread join回收線程退出值 11.pthread_cancel 12.殺死…

element-plus中,表單校驗的使用

目錄 一.案例1&#xff1a;給下面的表單添加校驗 1.目的要求 2.步驟 ①給需要校驗的el-form-item項&#xff0c;添加prop屬性 ②定義一個表單校驗對象&#xff0c;里面存放了每一個prop的檢驗規則 ③給el-form組件&#xff0c;添加:rules屬性 ④給el-form組件&#xff0…

團體設計程序天梯賽L2-025 # 分而治之

文章目錄 題目解讀輸入格式輸出格式 思路Ac Code參考 題目解讀 在戰爭中&#xff0c;我們希望首先攻下敵方的部分城市&#xff0c;使其剩余的城市變成孤立無援&#xff0c;然后再分頭各個擊破。為此參謀部提供了若干打擊方案。本題就請你編寫程序&#xff0c;判斷每個方案的可…

Arduino示例代碼講解:Knock Sensor 敲擊感知器

Arduino示例代碼講解:Knock Sensor 敲擊感知器 Knock Sensor 敲擊感知器功能概述硬件部分:軟件部分:代碼逐行解釋定義常量定義變量`setup()` 函數`loop()` 函數工作原理Knock Sensor 敲擊感知器 這段代碼是一個Arduino示例程序,用于檢測敲擊聲。它通過讀取一個壓電元件(p…

【百日精通JAVA | SQL篇 | 第三篇】 MYSQL增刪改查

SQL得最核心就是增刪改查 一個后端開發&#xff0c;在工作中&#xff0c;最常見的場景就是CRUD。 插入數據 insert into student values (1,zhangsan); 指定列插入數據 同時多個列明之間使用逗號&#xff0c;來分割 insert into student (name) values (zhaoliu); 這個黑框…

ggscitable包通過曲線擬合深度挖掘一個陌生數據庫非線性關系

很多新手剛才是總是覺得自己沒什么可以寫的&#xff0c;自己不知道選什么題材進行分析&#xff0c;使用scitable包ggscitable包后這個完全不用擔心&#xff0c;選題多到你只會擔心你寫不完&#xff0c;寫得不夠快。 既往咱們使用scitable包交互效應深度挖掘一個陌生數據庫&…

ctfshow VIP題目限免 版本控制泄露源碼2

根據題目提示是版本控制泄露源碼 版本控制&#xff08;Version Control&#xff09;是一種在軟件開發和其他領域中廣泛使用的技術&#xff0c;用于管理文件或項目的變更歷史。 主流的版本控制工具&#xff1a; ?Git?&#xff1a;目前最流行的分布式版本控制系統。?SVN?&am…

2025-04-05 吳恩達機器學習5——邏輯回歸(2):過擬合與正則化

文章目錄 1 過擬合1.1 過擬合問題1.2 解決過擬合 2 正則化2.1 正則化代價函數2.2 線性回歸的正則化2.3 邏輯回歸的正則化 1 過擬合 1.1 過擬合問題 欠擬合&#xff08;Underfitting&#xff09; 模型過于簡單&#xff0c;無法捕捉數據中的模式&#xff0c;導致訓練誤差和測試誤…

如何用人工智能大模型,進行作業批改?

今天我們學習人工智能大模型如何進行作業批改。手把手學習視頻請訪問https://edu.csdn.net/learn/40402/666452 第一步&#xff0c;進入訊飛星火。打開google瀏覽器&#xff0c;輸入百度地址后&#xff0c;搜索”訊飛星火”&#xff0c;在搜索的結果中&#xff0c;點第一個訊飛…

C++學習筆記之 模板|函數模板|類模板

函數模板 類模板 定義&#xff1a;函數模板是建立一個通用函數&#xff0c;它所用到的數據的類型&#xff08;包括返回值類型、形參類型、局部變量類型 &#xff09;可以不具體指定&#xff0c;而是用一個虛擬的類型來代替&#xff08;用標識符占位&#xff09;&#xff0c;在…

正則入門到精通

? 一、正則表達式入門? 正則表達式本質上是一串字符序列&#xff0c;用于定義一個文本模式。通過這個模式&#xff0c;我們可以指定要匹配的文本特征。例如&#xff0c;如果你想匹配一個以 “abc” 開頭的字符串&#xff0c;正則表達式可以寫作 “^abc”&#xff0c;其中 …

對備忘錄模式的理解

對備忘錄模式的理解 一、場景1、題目【[來源](https://kamacoder.com/problempage.php?pid1095)】1.1 題目描述1.2 輸入描述1.3 輸出描述1.4 輸入示例1.5 輸出示例 2、理解需求 二、不采用備忘錄設計模式1、代碼2、問題3、錯誤的備忘錄模式 三、采用備忘錄設計模式1、代碼1.1 …

86.方便的double轉string屬性 C#例子 WPF例子

在C#開發中&#xff0c;屬性封裝是一種常見的設計模式&#xff0c;它可以幫助我們更好地控制數據的訪問和修改&#xff0c;同時提供更靈活的功能擴展。今天&#xff0c;我們就來探討一個簡單而優雅的屬性封裝示例&#xff1a;Power 和 PowerFormatted。 1. 問題背景 在實際開…

bun 版本管理工具 bum 安裝與使用

在使用 node 的過程中&#xff0c;我們可能會因為版本更新或者不同項目的要求而頻繁切換 node 版本&#xff0c;或者是希望使用更簡單的方式安裝不同版本的 node&#xff0c;這個時候我們一般會用到 nvm 或者類似的工具。 在我嘗試使用 bun 的時候&#xff0c;安裝前第一個想到…

GRE,MGRE

GRE&#xff1a;靜態過程&#xff0c;有局限性 R1 &#xff1a; [r1]interface Tunnel 0/0/0 --- 創建一個虛擬的隧道接口 [r1-Tunnel0/0/0]ip address 192.168.3.1 24 --- 給隧道接口分配一個 IP 地址 [r1-Tunnel0/0/0]tunnel-protocol gre --- 定義接口的封裝方式 [r1-Tun…

游戲無法啟動?XINPUT1_3.dll 丟失的終極解決方案

當你興奮地啟動一款新游戲時&#xff0c;突然彈出一個錯誤提示——‘程序無法啟動&#xff0c;因為計算機中丟失 XINPUT1_3.dll’。這種問題在 PC 玩家中非常常見&#xff0c;尤其是運行一些較老的游戲時。XINPUT1_3.dll 是 DirectX 運行庫的關鍵組件&#xff0c;缺失會導致游戲…

用大語言模型學文學常識

李白的詩句“右軍本清真”中的“清真”并非指伊斯蘭教信仰&#xff0c;而是對王羲之&#xff08;王右軍&#xff09;人格和藝術境界的贊美。以下是對這一問題的詳細分析&#xff1a; “清真”的古代含義 在魏晉至唐代的語境中&#xff0c;“清真”一詞多用于形容人的品格高潔、…

css炫酷的3D水波紋文字效果實現詳解

炫酷的3D水波紋文字效果實現詳解 這里寫目錄標題 炫酷的3D水波紋文字效果實現詳解項目概述技術棧核心實現1. 基礎布局2. 漸變背景3. 文字效果實現3.1 基礎樣式3.2 文字漂浮動畫 4. 水波紋效果4.1 模糊效果4.2 水波動畫 5. 交互效果 技術要點項目難點與解決方案總結 項目概述 在…