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

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

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

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

自定義注解有哪些好處?

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

自定義注解的原理

Spring Boot 自定義注解的底層原理主要依賴(lài)于:

  • Java 注解機(jī)制(@interface 定義注解)
  • AOP(面向切面編程) 或 攔截器 結(jié)合反射來(lái)解析注解
  • Spring 容器在運(yùn)行時(shí)自動(dòng)識(shí)別和織入邏輯

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

引入依賴(lài)

首先確保pom.xml中包含必要的依賴(lài):

<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>

定義自定義注解

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

import java.lang.annotation.*;

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

注解說(shuō)明:

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

常見(jiàn)的自定義注解案例

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

自定義日志注解

定義注解

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

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

切面實(shí)現(xiàn)

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

@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();
        
        // 記錄開(kāi)始時(shí)間
        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í)行耗時(shí): {}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();
    }
}

使用示例

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

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

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

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

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

定義注解

效果:提交手機(jī)號(hào)不合法時(shí),自動(dòng)拋出校驗(yàn)異常

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

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

實(shí)現(xiàn)校驗(yàn)器

這里就簡(jiǎn)單驗(yàn)證一下是否正確的手機(jī)號(hào),小伙伴們可以加入自己需要的驗(yàn)證邏輯,比如僅限移動(dòng)用戶(hù)等

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 "注冊(cè)成功";
    }

    public static class UserDTO {
        @NotBlank
        private String name;

        @Phone
        private String phone;

        // getter/setter
    }
}

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

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

這里我們就模擬一下全縣校驗(yàn)的功能

定義注解

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

實(shí)現(xiàn) AOP 權(quán)限校驗(yà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)前用戶(hù)權(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 "管理員頁(yè)面";
    }
}

自定義分布式限流注解

定義注解

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

切面實(shí)現(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é)合用戶(hù)信息、IP等生成唯一key
            return "rate_limit:" + System.identityHashCode(rateLimit);
        }
        return "rate_limit:" + key;
    }
}

// 簡(jiǎn)單的令牌桶限流器實(shí)現(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("公開(kāi)數(shù)據(jù)");
    }
    
    @PostMapping("/submit")
    @RateLimit(key = "submit_limit", limit = 5, timeWindow = 30)
    public ApiResponse submitData(@RequestBody Data data) {
        // 處理提交
        return ApiResponse.success("提交成功");
    }
}

自定義加解密注解

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

總結(jié)

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

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

相關(guān)文章

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

    Cookie 實(shí)現(xiàn)的原理

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

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

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

    Java面試題目集錦

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

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

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

    Java實(shí)現(xiàn)動(dòng)態(tài)生成GIF圖像詳解

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

    java HashMap擴(kuò)容詳解及實(shí)例代碼

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

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

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

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

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

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

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

    深入Sqlite多線(xiàn)程入庫(kù)的問(wèn)題

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

最新評(píng)論

临夏县| 福建省| 交城县| 淮阳县| 沽源县| 衡南县| 高邮市| 河曲县| 盐源县| 江阴市| 丰县| 焦作市| 太原市| 包头市| 新田县| 开阳县| 阳朔县| 汝阳县| 闵行区| 深泽县| 广灵县| 乐都县| 泽普县| 特克斯县| 馆陶县| 拜城县| 宝丰县| 玉龙| 竹山县| 阜康市| 衢州市| 迁安市| 古浪县| 娄底市| 祁东县| 醴陵市| 土默特左旗| 富蕴县| 连山| 鹿泉市| 惠水县|