最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

SpringBoot自定義注解的5個實戰(zhàn)案例分享

 更新時間:2025年09月29日 08:56:06   作者:Micro麥可樂  
自定義注解是一種強大的元編程工具,允許在不修改原有代碼邏輯的情況下,為程序添加額外的功能,本文將分析5個常見的案例,希望對大家有所幫助

自定義注解是一種強大的元編程工具,允許在不修改原有代碼邏輯的情況下,為程序添加額外的功能。通過AOP面向切面編程)與自定義注解的結(jié)合,我們可以實現(xiàn)關(guān)注點分離,讓業(yè)務(wù)代碼更加清晰簡潔。

自定義注解有哪些好處?

  • 代碼復(fù)用:將通用邏輯封裝到注解中
  • 業(yè)務(wù)解耦:橫切關(guān)注點與核心業(yè)務(wù)邏輯分離
  • 聲明式編程:通過注解配置行為,代碼更直觀
  • 可維護性:通用邏輯集中管理,修改更方便

自定義注解的原理

Spring Boot 自定義注解的底層原理主要依賴于:

  • Java 注解機制(@interface 定義注解)
  • AOP(面向切面編程) 或 攔截器 結(jié)合反射來解析注解
  • Spring 容器在運行時自動識別和織入邏輯

自定義注解的實現(xiàn)步驟

引入依賴

首先確保pom.xml中包含必要的依賴:

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

定義自定義注解

以一個最基礎(chǔ)的自定義注解為例:

import java.lang.annotation.*;

@Target(ElementType.METHOD) // 注解作用目標(biāo):方法
@Retention(RetentionPolicy.RUNTIME) // 運行時生效
@Documented
public @interface MyAnnotation {
    String value() default "default";
}

注解說明:

  • @Target:指定注解作用的范圍(類、方法、字段、參數(shù)…)
  • @Retention:指定注解生命周期(源碼、編譯期、運行時)
  • @Documented:生成 Javadoc 時包含注解信息

常見的自定義注解案例

下面博主講完整演示幾個日常開發(fā)中我們常見的自定義注解案例來讓大家深入的了解

自定義日志注解

定義注解

效果:調(diào)用接口時,自動打印方法耗時和相關(guān)日志

/**
 * 方法日志注解
 * 用于自動記錄方法入?yún)?、出參和?zhí)行時間
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MethodLog {
    String value() default "";
    boolean printArgs() default true;
    boolean printResult() default true;
    boolean timing() default true;
}

切面實現(xiàn)

這里僅僅以打印輸出為案例,實際生產(chǎn)環(huán)境中,小伙伴們可以結(jié)合數(shù)據(jù)庫、日志系統(tǒng)等將信息記錄入庫

@Aspect
@Component
@Slf4j
public class MethodLogAspect {

    @Around("@annotation(methodLog)")
    public Object around(ProceedingJoinPoint joinPoint, MethodLog methodLog) throws Throwable {
        String methodName = getMethodName(joinPoint);
        String className = joinPoint.getTarget().getClass().getSimpleName();
        
        // 記錄開始時間
        long startTime = System.currentTimeMillis();
        
        if (methodLog.printArgs()) {
            Object[] args = joinPoint.getArgs();
            log.info("[{}#{}] 方法調(diào)用, 參數(shù): {}", className, methodName, Arrays.toString(args));
        } else {
            log.info("[{}#{}] 方法調(diào)用", className, methodName);
        }
        
        try {
            Object result = joinPoint.proceed();
            
            if (methodLog.printResult()) {
                log.info("[{}#{}] 方法返回: {}", className, methodName, result);
            }
            
            if (methodLog.timing()) {
                long cost = System.currentTimeMillis() - startTime;
                log.info("[{}#{}] 方法執(zhí)行耗時: {}ms", className, methodName, cost);
            }
            
            return result;
        } catch (Exception e) {
            log.error("[{}#{}] 方法執(zhí)行異常: {}", className, methodName, e.getMessage());
            throw e;
        }
    }
    
    private String getMethodName(ProceedingJoinPoint joinPoint) {
        return joinPoint.getSignature().getName();
    }
}

使用示例

以用戶接口為例,創(chuàng)建用戶的時候會記錄該接口會打印相關(guān)的信息日志

@RestController
@RequestMapping("/api/user")
public class UserController {
    
    @PostMapping
    @MethodLog(value = "創(chuàng)建用戶", printArgs = true, printResult = true, timing = true)
    public User createUser(@RequestBody User user) {
        // 業(yè)務(wù)邏輯
        return userService.save(user);
    }
    
    @GetMapping("/{id}")
    @MethodLog("根據(jù)ID查詢用戶")
    public User getUser(@PathVariable Long id) {
        return userService.findById(id);
    }
}

自定義參數(shù)校驗注解

通常我們在Controller中進行數(shù)據(jù)校驗都是用validation, 可以大大節(jié)省我們參數(shù)校驗的時間,雖然validation 默認(rèn)的注解已經(jīng)足以應(yīng)付我們工作中大部分場景,但還是會有一些參數(shù)校驗有其它的一些驗證要求,那么就可以用到自定義參數(shù)校驗注解。

你也查閱博主之前寫的 【Spring Boot數(shù)據(jù)校驗validation實戰(zhàn):寫少一半代碼,還更優(yōu)雅!】學(xué)習(xí)Spring Boot數(shù)據(jù)校驗

定義注解

效果:提交手機號不合法時,自動拋出校驗異常

import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.*;

@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = PhoneValidator.class) // 綁定校驗器
public @interface Phone {
    String message() default "手機號格式錯誤";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}

實現(xiàn)校驗器

這里就簡單驗證一下是否正確的手機號,小伙伴們可以加入自己需要的驗證邏輯,比如僅限移動用戶等

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

public class PhoneValidator implements ConstraintValidator<Phone, String> {

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        return value != null && value.matches("^1[3-9]\\d{9}$");
    }
}

使用示例

import javax.validation.Valid;
import javax.validation.constraints.NotBlank;

@RestController
public class RegisterController {

    @PostMapping("/register")
    public String register(@Valid @RequestBody UserDTO userDTO) {
        return "注冊成功";
    }

    public static class UserDTO {
        @NotBlank
        private String name;

        @Phone
        private String phone;

        // getter/setter
    }
}

自定義權(quán)限校驗注解

本次我們模擬Spring Security中的@PreAuthorize注解,想完整學(xué)習(xí)@PreAuthorize注解用法的小伙伴可以參考博主Spring Security專欄下的 【最新Spring Security實戰(zhàn)教程(七)方法級安全控制@PreAuthorize注解的靈活運用】

這里我們就模擬一下全縣校驗的功能

定義注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CheckPermission {
    String value(); // 權(quán)限標(biāo)識
}

實現(xiàn) AOP 權(quán)限校驗

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class PermissionAspect {

    @Before("@annotation(checkPermission)")
    public void check(JoinPoint joinPoint, CheckPermission checkPermission) {
        String requiredPermission = checkPermission.value();
        // 模擬從上下文獲取當(dāng)前用戶權(quán)限
        String userPermission = "USER"; 

        if (!userPermission.equals(requiredPermission)) {
            throw new RuntimeException("權(quán)限不足,缺少:" + requiredPermission);
        }
    }
}

使用示例

@RestController
public class AdminController {

    @CheckPermission("ADMIN")
    @GetMapping("/admin")
    public String adminPage() {
        return "管理員頁面";
    }
}

自定義分布式限流注解

定義注解

/**
 * 限流注解
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RateLimit {
    String key() default "";
    int limit() default 100;
    int timeWindow() default 60; // 時間窗口,單位:秒
    String message() default "訪問過于頻繁,請稍后再試";
}

切面實現(xiàn)

@Aspect
@Component
@Slf4j
public class RateLimitAspect {
    
    private final Map<String, RateLimiter> limiterMap = new ConcurrentHashMap<>();
    
    @Before("@annotation(rateLimit)")
    public void rateLimitCheck(RateLimit rateLimit) {
        String key = generateKey(rateLimit);
        RateLimiter limiter = limiterMap.computeIfAbsent(key, 
            k -> RateLimiter.create(rateLimit.limit() / (double) rateLimit.timeWindow()));
        
        if (!limiter.tryAcquire()) {
            throw new RuntimeException(rateLimit.message());
        }
    }
    
    private String generateKey(RateLimit rateLimit) {
        String key = rateLimit.key();
        if (StringUtils.isEmpty(key)) {
            // 可以結(jié)合用戶信息、IP等生成唯一key
            return "rate_limit:" + System.identityHashCode(rateLimit);
        }
        return "rate_limit:" + key;
    }
}

// 簡單的令牌桶限流器實現(xiàn)
class RateLimiter {
    private final double capacity;
    private final double refillTokensPerOneMillis;
    private double availableTokens;
    private long lastRefillTimestamp;
    
    public static RateLimiter create(double permitsPerSecond) {
        return new RateLimiter(permitsPerSecond);
    }
    
    private RateLimiter(double permitsPerSecond) {
        this.capacity = permitsPerSecond;
        this.refillTokensPerOneMillis = permitsPerSecond / 1000.0;
        this.availableTokens = permitsPerSecond;
        this.lastRefillTimestamp = System.currentTimeMillis();
    }
    
    public synchronized boolean tryAcquire() {
        refill();
        if (availableTokens < 1) {
            return false;
        }
        availableTokens -= 1;
        return true;
    }
    
    private void refill() {
        long currentTime = System.currentTimeMillis();
        if (currentTime > lastRefillTimestamp) {
            long millisSinceLastRefill = currentTime - lastRefillTimestamp;
            double refill = millisSinceLastRefill * refillTokensPerOneMillis;
            this.availableTokens = Math.min(capacity, availableTokens + refill);
            this.lastRefillTimestamp = currentTime;
        }
    }
}

使用示例

@RestController
@RequestMapping("/api")
public class ApiController {
    
    @GetMapping("/public/data")
    @RateLimit(limit = 10, timeWindow = 60, message = "接口調(diào)用頻率超限")
    public ApiResponse getPublicData() {
        return ApiResponse.success("公開數(shù)據(jù)");
    }
    
    @PostMapping("/submit")
    @RateLimit(key = "submit_limit", limit = 5, timeWindow = 30)
    public ApiResponse submitData(@RequestBody Data data) {
        // 處理提交
        return ApiResponse.success("提交成功");
    }
}

自定義加解密注解

可參考博主之前寫的 【Spring Boot中整合Jasypt 使用自定義注解+AOP實現(xiàn)敏感字段的加解密】進行學(xué)習(xí),這里就不再贅述了!

總結(jié)

以上通過5個案例演示,完整講解了Spring Boot 自定義注解的使用,通過合理使用自定義注解,我們可以大幅提升代碼的可讀性、可維護性和復(fù)用性。在實際項目中,可以根據(jù)業(yè)務(wù)需求靈活組合和擴展這些注解,構(gòu)建更加健壯和安全的應(yīng)用程序。

到此這篇關(guān)于SpringBoot自定義注解的5個實戰(zhàn)案例分享的文章就介紹到這了,更多相關(guān)SpringBoot自定義注解內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Cookie 實現(xiàn)的原理

    Cookie 實現(xiàn)的原理

    我們在瀏覽器中,經(jīng)常涉及到數(shù)據(jù)的交換,比如你登錄郵箱,登錄一個頁面。我們經(jīng)常會在此時設(shè)置30天內(nèi)記住我,或者自動登錄選項。那么它們是怎么記錄信息的呢,答案就是今天的主角cookie了
    2021-06-06
  • SpringBoot實現(xiàn)自定義指標(biāo)監(jiān)控功能

    SpringBoot實現(xiàn)自定義指標(biāo)監(jiān)控功能

    本文主要介紹了SpringBoot實現(xiàn)自定義指標(biāo)監(jiān)控功能的實現(xiàn),,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,感興趣的小伙伴跟著著小編來一起來學(xué)習(xí)吧
    2024-01-01
  • Java面試題目集錦

    Java面試題目集錦

    本文是小編日常收集整理的java面試題目,非常不錯,具有參考借鑒價值,需要的朋友可以參考下
    2016-09-09
  • Springboot2.x+ShardingSphere實現(xiàn)分庫分表的示例代碼

    Springboot2.x+ShardingSphere實現(xiàn)分庫分表的示例代碼

    這篇文章主要介紹了Springboot2.x+ShardingSphere實現(xiàn)分庫分表的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-10-10
  • Java實現(xiàn)動態(tài)生成GIF圖像詳解

    Java實現(xiàn)動態(tài)生成GIF圖像詳解

    在互聯(lián)網(wǎng)上有許多有趣的場景,其中的一種就是動圖。這不是視頻,而是一種GIF圖像信息。本文將利用Java實現(xiàn)動態(tài)生成GIF圖像功能,需要的可以參考一下
    2022-09-09
  • java  HashMap擴容詳解及實例代碼

    java HashMap擴容詳解及實例代碼

    這篇文章主要介紹了java HashMap擴容詳解及實例代碼的相關(guān)資料,需要的朋友可以參考下
    2017-02-02
  • Java?常量池詳解之字符串常量池實現(xiàn)代碼

    Java?常量池詳解之字符串常量池實現(xiàn)代碼

    這篇文章主要介紹了Java?常量池詳解之字符串常量池,本文結(jié)合示例代碼對java字符串常量池相關(guān)知識講解的非常詳細(xì),需要的朋友可以參考下
    2022-12-12
  • Java實現(xiàn)ODT轉(zhuǎn)PDF的常用方案詳解

    Java實現(xiàn)ODT轉(zhuǎn)PDF的常用方案詳解

    在?Java?開發(fā)中,文檔格式轉(zhuǎn)換是一個常見的技術(shù)需求,ODT?是基于?XML?的開放文檔格式標(biāo)準(zhǔn),下面我們就來看看如何使用Java實現(xiàn)ODT轉(zhuǎn)PDF吧
    2026-04-04
  • Java使用正則表達式匹配獲取鏈接地址的方法示例

    Java使用正則表達式匹配獲取鏈接地址的方法示例

    這篇文章主要介紹了Java使用正則表達式匹配獲取鏈接地址的方法,簡單分析了java正則匹配常用方法及獲取網(wǎng)址鏈接的相關(guān)操作技巧,需要的朋友可以參考下
    2017-08-08
  • 深入Sqlite多線程入庫的問題

    深入Sqlite多線程入庫的問題

    本篇文章是對Sqlite多線程入庫的問題進行了詳細(xì)的分析介紹,需要的朋友參考下
    2013-05-05

最新評論

宜川县| 那坡县| 方正县| 大姚县| 旌德县| 萨嘎县| 通河县| 宜章县| 柯坪县| 陆丰市| 崇文区| 富平县| 建平县| 望奎县| 池州市| 汝城县| 托克托县| 调兵山市| 遂溪县| 榕江县| 陈巴尔虎旗| 应城市| 澄迈县| 龙川县| 海南省| 讷河市| 开封市| 彭阳县| 万安县| 威海市| 嘉兴市| 若羌县| 保山市| 夏河县| 昆明市| 新疆| 巴彦淖尔市| 观塘区| 科技| 永福县| 志丹县|