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.mzph.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. 交互效果 技术要点项目难点与解决方案总结 项目概述 在…