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

Spring?Boot?自定義注解實戰(zhàn)案例詳解

 更新時間:2026年05月23日 16:03:40   作者:Micro麥可樂  
自定義注解在SpringBoot中的強大應用,通過AOP與注解結合實現(xiàn)代碼復用、業(yè)務解耦與聲明式編程,簡化業(yè)務邏輯,本文詳細解析了日志記錄、參數(shù)校驗、權限校驗與分布式限流等注解的定義與實現(xiàn),助你提升代碼質量與開發(fā)效率

@RestController@Autowired@Transactional

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

自定義注解有哪些好處?

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

自定義注解的原理

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

  • Java 注解機制(@interface 定義注解)
  • AOP(面向切面編程) 或 攔截器 結合反射來解析注解
  • 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>

定義自定義注解

以一個最基礎的自定義注解為例:

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

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

常見的自定義注解案例

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

? 自定義日志注解

定義注解
效果:調用接口時,自動打印方法耗時和相關日志

/**
 * 方法日志注解
 * 用于自動記錄方法入?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)境中,小伙伴們可以結合數(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("[{}#{}] 方法調用, 參數(shù): {}", className, methodName, Arrays.toString(args));
        } else {
            log.info("[{}#{}] 方法調用", 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)建用戶的時候會記錄該接口會打印相關的信息日志

@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è)務邏輯
        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 默認的注解已經(jīng)足以應付我們工作中大部分場景,但還是會有一些參數(shù)校驗有其它的一些驗證要求,那么就可以用到自定義參數(shù)校驗注解。

你也查閱博主之前寫的 【Spring Boot數(shù)據(jù)校驗validation實戰(zhàn):寫少一半代碼,還更優(yōu)雅!】學習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
    }
}

? 自定義權限校驗注解

本次我們模擬Spring Security中的@PreAuthorize注解,想完整學習@PreAuthorize注解用法的小伙伴可以參考博主Spring Security專欄下的 Spring Security方法級安全控制@PreAuthorize注解的靈活運用小結
這里我們就模擬一下全縣校驗的功能

定義注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CheckPermission {
    String value(); // 權限標識
}

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

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();
        // 模擬從上下文獲取當前用戶權限
        String userPermission = "USER"; 
        if (!userPermission.equals(requiredPermission)) {
            throw new RuntimeException("權限不足,缺少:" + 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)) {
            // 可以結合用戶信息、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 = "接口調用頻率超限")
    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("提交成功");
    }
}

? 自定義加解密注解

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

總結

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

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

相關文章

  • Java使用JCommander實現(xiàn)解析命令行參數(shù)

    Java使用JCommander實現(xiàn)解析命令行參數(shù)

    jcommander?是一個只有幾十?kb?的?Java?命令行參數(shù)解析工具,可以通過注解的方式快速實現(xiàn)命令行參數(shù)解析,本文就來和大家介紹一下JCommander是如何解析命令行參數(shù)吧
    2023-06-06
  • IDEA項目如何取消git版本管控并添加svn版本控制

    IDEA項目如何取消git版本管控并添加svn版本控制

    在公司內部服務器環(huán)境下,將代碼倉庫從Gitee的Git遷移到SVN可以避免外部版本控制的風險,遷移過程中,先刪除項目的.git文件夾,再通過Eclipse的設置界面刪除原Git配置并添加SVN配置,之后,將項目提交到SVN倉庫,確保使用ignore列表過濾不必要的文件
    2024-10-10
  • Tomcat集群和Session復制應用介紹

    Tomcat集群和Session復制應用介紹

    本文將詳細介紹Tomcat集群和Session復制應用,需要了解的朋友可以參考下
    2012-11-11
  • mybatis如何批量添加一對多中間表

    mybatis如何批量添加一對多中間表

    這篇文章主要介紹了mybatis如何批量添加一對多中間表,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-02-02
  • Spring Boot 通過注解實現(xiàn)數(shù)據(jù)校驗的方法

    Spring Boot 通過注解實現(xiàn)數(shù)據(jù)校驗的方法

    這篇文章主要介紹了Spring Boot 通過注解實現(xiàn)數(shù)據(jù)校驗的方法,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-09-09
  • 一文教你如何使用Java靈活讀取Excel內容

    一文教你如何使用Java靈活讀取Excel內容

    這篇文章主要給大家介紹了關于如何使用Java靈活讀取Excel內容的相關資料,讀取Excel數(shù)據(jù)是Java開發(fā)中常見的任務,文中通過代碼示例介紹的非常詳細,需要的朋友可以參考下
    2023-09-09
  • Java?C++題解leetcode消失的兩個數(shù)字實例

    Java?C++題解leetcode消失的兩個數(shù)字實例

    這篇文章主要介紹了Java?C++題解leetcode消失的兩個數(shù)字實例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-09-09
  • java遠程調用接口、URL的方式代碼

    java遠程調用接口、URL的方式代碼

    我們都知道接口有自己本地的,也有遠程別人寫好的,而調用遠程接口的就需要使用遠程調用啦,這篇文章主要給大家介紹了關于java遠程調用接口、URL的相關資料,文中通過代碼介紹的非常詳細,需要的朋友可以參考下
    2023-11-11
  • java byte數(shù)組與16進制間相互轉換的示例

    java byte數(shù)組與16進制間相互轉換的示例

    這篇文章主要介紹了java byte數(shù)組與16進制間相互轉換的示例,幫助大家更好的理解和使用Java,感興趣的朋友可以了解下
    2020-10-10
  • 解讀tk.mybatis的通用批量更新方式

    解讀tk.mybatis的通用批量更新方式

    這篇文章主要介紹了關于tk.mybatis的通用批量更新方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-08-08

最新評論

天等县| 盱眙县| 班戈县| 镇坪县| 绥中县| 潜山县| 平泉县| 罗田县| 惠水县| 耿马| 新乡市| 凤台县| 新津县| 乌苏市| 镇赉县| 崇阳县| 无棣县| 漠河县| 东丰县| 涿州市| 宜兰市| 英吉沙县| 龙井市| 宁明县| 溆浦县| 海城市| 凉山| 邮箱| 布尔津县| 塘沽区| 百色市| 天台县| 内丘县| 阳谷县| 遂川县| 都安| 商河县| 宜君县| 德庆县| 玉山县| 定安县|