Java如何自定义注解及在SpringBoot中的应用

注解

注解(Annotation),也叫元数据。一种代码级别的说明。它是JDK1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。注解相关类都包含在java.lang.annotation包中。
注解可以看作是一种特殊的标记,可以用在方法、类、参数和包上,程序在编译或者运行时可以检测到这些标记而进行一些特殊的处理。

Java注解分类:基本注解,元注解,自定义注解
JDK基本注解:

@Override
重写
@SuppressWarnings(value = "unchecked")
压制编辑器警告

JDK元注解

//表示需要在什么级别保存该注释信息,用于描述注解的生命周期(即:被描述的注解在什么范围内有效)
//即:注解的生命周期。
@Retention:定义注解的保留策略
@Retention(RetentionPolicy.SOURCE)   //注解仅存在于源码中,在class字节码文件中不包含
@Retention(RetentionPolicy.CLASS)  //默认的保留策略,注解会在class字节码文件中存在,但运行时无法获得
@Retention(RetentionPolicy.RUNTIME)//注解会在class字节码文件中存在,在运行时可以通过反射获取到// 用于描述注解的使用范围(即:被描述的注解可以用在什么地方)。
@Target:指定被修饰的Annotation可以放置的位置(被修饰的目标)
@Target(ElementType.TYPE)                      //接口、类
@Target(ElementType.FIELD)                     //属性
@Target(ElementType.METHOD)                    //方法
@Target(ElementType.PARAMETER)                 //方法参数
@Target(ElementType.CONSTRUCTOR)               //构造函数
@Target(ElementType.LOCAL_VARIABLE)            //局部变量
@Target(ElementType.ANNOTATION_TYPE)           //注解
@Target(ElementType.PACKAGE)                   //包
注:可以指定多个位置,例如:
@Target({ElementType.METHOD, ElementType.TYPE}),也就是此注解可以在方法和类上面使用//表明使用了@Inherited注解的注解,所标记的类的子类也会拥有这个注解。
@Inherited:指定被修饰的Annotation将具有继承性//表明该注解标记的元素可以被Javadoc 或类似的工具文档化
@Documented:指定被修饰的该Annotation可以被javadoc工具提取成文档.

在这里插入图片描述
由此可见生命周期关系:SOURCE < CLASS < RUNTIME,我们一般用RUNTIME

其中最重要的是:

@Retention(RetentionPolicy.RUNTIME)
@Target:指定被修饰的Annotation可以放置的位置(被修饰的目标)

自定义注解

注解分类(根据Annotation是否包含成员变量,可以把Annotation分为两类):

标记Annotation:
没有成员变量的Annotation; 这种Annotation仅利用自身的存在与否来提供信息

元数据Annotation:
包含成员变量的Annotation; 它们可以接受(和提供)更多的元数据;

反射机制

反射最重要的就是类,一切反射的基础是类
静态语言 VS 动态语言

动态语言
是一类在运行时可以改变其结构的原因:例如新的函数、对象、甚至代码可以被引进,已有的函数可以被删除或是其他结构上的变化。通俗点说就是在运行时代码可以根据某些条件改变自身结构
主要动态语言:Object-c、C#、JavaScript、php、Python等

静态语言
与动态语言相对应的,运行时结构不可变的语言就是静态语言。如Java、C、C++。
Java不是动态语言,但Java可以称之为"准动态语言"。即Java有一定的动态性,我们可以利用反射机制获得类似语言的特性。Java的动态性让编程的时候更加灵活!

自定义注解参数可以支持的类型

1.八大基本数据类型

2.String类型

3.Class类型

4.enum类型

5.Annotation类型

6.以上所有类型的数组

如何处理注解

注解可以分两个主要阶段进行处理:
编译时处理
在编译时,注解可以由注解处理器处理 - 这些是特殊的类,可以读取注解信息并生成附加源代码、文档或其他资源。注解处理器是 Java 注解处理工具 (APT) 的一部分。

编译时处理的一个示例是@Override注解,Java 编译器使用它来验证某个方法确实覆盖了超类中的方法。

运行时处理
保留策略为RUNTIME 的注解在运行时可供 JVM 使用。这允许运行时反射——代码可以检查自己的注解,并根据这些注解的存在和配置执行不同步骤。

Java 中的注解是通过动态代理和接口实现的。当您查询元素上的注解时,返回的不是注解接口的直接实例,而是实现注解接口的代理。

自定义注解

定义注解:

要创建自定义注解,从@interface关键字开始。这向 Java 编译器发出信号,表明正在声明注解。以下是定义基本自定义注解的方法:

package com.wsl.annotitiondemo.annotation;import java.lang.annotation.*;/*** packageName com.wsl.annotitiondemo.annotation  OperationLog** @author victor* @version JDK 8* @date 2024/7/9* @description*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface OperationLog {String username() default "";OperationType type();String content() default "";}
package com.wsl.annotitiondemo.annotation;/*** packageName com.wsl.annotitiondemo.annotation  OperationType** @author victor* @version JDK 8* @date 2024/7/9* @description*/
public enum OperationType {//FIND(0,"find") ,//ADD(1,"add") ,//UPDATE(2,"update"),//DELETE(3,"delete");private final int number;private final String description;OperationType(int number,String description){this.number = number;this.description = description;}public int getNumber() {return number;}public String getDescription() {return description;}
}

在这个定义中:

@Retention(RetentionPolicy.RUNTIME)指定该注解在运行时可用于反射。
@Target({ElementType.METHOD, ElementType.TYPE})表明该注解既可以用于类声明,也可以用于方法声明。

应用注解:

定义注解后,您可以将其应用到代码中。例如:

package com.wsl.annotitiondemo.annotation;import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;import java.lang.reflect.Method;
import java.util.Optional;/*** packageName com.wsl.annotitiondemo.annotation  ComLogAspect** @author victor* @version JDK 8* @date 2024/7/9* @description TODO*/
@Component
@Aspect
public class ComLogAspect {//配置织入点@Pointcut("@annotation(com.wsl.annotitiondemo.annotation.OperationLog)")public void logPointCut(){}//@Before: 前置通知, 在方法执行之前执行,这个通知不能阻止连接点前的执行(除非它抛出一个异常)@Before("logPointCut()")public  void doBefore(JoinPoint joinPoint){System.out.println("dobefore");handleLog(joinPoint,null);}
//
//    //@After: 后置通知, 在方法执行之后执行(不论是正常返回还是异常退出)。
//    @After(value = "logPointCut() && @annotation(operationLog)")
//    public  void doAfter(OperationLog operationLog){
//
//    }/*** @Around: 包围一个连接点(join point)的通知,如方法调用。这是最强大的一种通知类型。* 环绕通知可以在方法调用前后完成自定义的行为。* 它也会选择是否继续执行连接点或直接返回它们自己的返回值或抛出异常来结束执行。* */@Around("logPointCut()")public  Object doAround(ProceedingJoinPoint pjp) throws Throwable {long startTime = System.currentTimeMillis();Object[] args = pjp.getArgs();System.out.println(Arrays.toString(args));Object ob = pjp.proceed();   // ob 为方法的返回值System.out.println("耗时 : " + (System.currentTimeMillis() - startTime) + "ms");return ob;}//@AfterRunning:返回通知, 在方法正常返回结果之后执行@AfterReturning(pointcut = "logPointCut()")public  void doAfterRunning(JoinPoint joinPoint){System.out.println("方法执行完执行...afterRunning");handleLog(joinPoint,null);}//@AfterThrowing: 异常通知, 在方法抛出异常之后。@AfterThrowing(value = "logPointCut()",throwing = "e")public  void doAfterThrowing(JoinPoint joinPoint,Exception e){handleLog(joinPoint,e);}private void handleLog(JoinPoint joinPoint,Exception ex){try {OperationLog operationLog = getAnnotationLog(joinPoint);if (operationLog==null){return ;}String className = joinPoint.getTarget().getClass().getName();String methodName = joinPoint.getSignature().getName();OperationType type = operationLog.type();String content = operationLog.content();String username = operationLog.username();System.out.println("==className=="+className);System.out.println("==methodName=="+methodName);System.out.println("==content=="+content);System.out.println("==username=="+username);}catch (Exception es){es.printStackTrace();}}private static OperationLog getAnnotationLog(JoinPoint joinPoint) throws Exception{Signature signature = joinPoint.getSignature();MethodSignature methodSignature= (MethodSignature)signature;Method method = methodSignature.getMethod();if (method!=null){return method.getAnnotation(OperationLog.class);}return null;}}

处理注解

自定义注解可以通过两种方式处理:在编译时使用注解处理器或在运行时使用反射。

编译时处理
要在编译时处理注解,通常会使用 Java 注解处理 API。注解处理器是一种检查和处理 Java 代码中注解类型的工具。这是注解处理器的骨架结构:

import javax.annotation.processing.AbstractProcessor; 
import javax.annotation.processing.RoundEnvironmentimport javax.lang.model.element.TypeElement; 
import javax.lang.model.element.Element; 
import java.util.Setpublic  class  MyAnnotationProcessor  extends  AbstractProcessor { @Override public  boolean  process (Set<? extends TypeElement> comments, RoundEnvironment roundEnv) { for (TypeElement comment : comments) { for (Element element : roundEnv.getElementsAnnotatedWith(annotation)) { // 处理} }返回 true} 
}

运行时处理
对于运行时处理,您将使用反射,如上一节所示。这是一个重点关注我们的自定义注解的简短示例:

import java.lang.reflect.Methodpublic  class  AnnotationRuntimeProcessor { public  static  void  processAnnotations (Class<?> clazz)  throws Exception { if (clazz.isAnnotationPresent(MyCustomAnnotation.class)) { // 类级注解MyCustomAnnotation  classAnnotation  = clazz.getAnnotation(MyCustomAnnotation.class); } System.out.println( "Class " + clazz.getSimpleName() + " 注解为: " + classAnnotation.author()); } for (Method method : clazz.getDeclaredMethods()) { if (method.isAnnotationPresent(MyCustomAnnotation.class)) { // 方法级注解MyCustomAnnotation  methodAnnotation  = method.getAnnotation(MyCustomAnnotation.class); System.out.println( "方法 " + method.getName() + " 注解为: " + methodAnnotation.author()); } } } public  static  void  main (String[] args)  throws Exception { processAnnotations(SomeClass.class); } 
}

在 Java 中创建和使用自定义注解是增强代码的有效方法。它允许更清晰、更具描述性的代码,并在编译时和运行时实现某些任务的自动化。通过定义、应用和处理自定义注解,您可以创建更具可读性、可维护性和表现力的 Java 应用程序。

测试:

package com.wsl.annotitiondemo.controller;import com.wsl.annotitiondemo.annotation.OperationLog;
import com.wsl.annotitiondemo.annotation.OperationType;
import org.springframework.stereotype.Component;/*** packageName com.wsl.annotitiondemo.controller  LogController** @author victor* @version JDK 8* @date 2024/7/9* @description*/
@Component
public class LogController {@OperationLog(username = "wsl",type = OperationType.ADD,content ="新增内容")public void testlog(){System.out.println("第一个测试");}
}
package com.wsl.annotitiondemo.controller;import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;/*** packageName com.wsl.annotitiondemo.controller  LogControllerTest** @author victor* @version JDK 8* @date 2024/7/9* @description TODO*/
@RunWith(SpringRunner.class)
@SpringBootTest
public class LogControllerTest {@Autowiredprivate LogController logController;@Testpublic  void  test01(){logController.testlog();}
}

添加的pom文件:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.7.3</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.wsl</groupId><artifactId>annotitiondemo</artifactId><version>0.0.1-SNAPSHOT</version><name>annotitiondemo</name><description>annotitiondemo</description><url/><licenses><license/></licenses><developers><developer/></developers><scm><connection/><developerConnection/><tag/><url/></scm><properties><java.version>22</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-test</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-test</artifactId></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build></project>

总结

注解是 Java 中的一项强大功能,它允许开发人员编写更清晰、更具表现力的代码。它们可用于多种目的,从向编译器提供提示,到启用复杂的运行时基于反射的逻辑。通过理解和利用自定义注解,Java 开发人员可以创建更健壮、可维护且防错的应用程序。

创建自定义注解涉及使用关键字定义注解、指定任何默认元素值以及使用和元注解@Retention,@Target,确定可以应用注解的位置。定义后,自定义注解可以在编译时或运行时使用反射进行处理,从而提供强大的机制来扩展 Java 语言的功能。

参考:
https://www.jb51.net/program/315875pzt.htm
https://zhuanlan.zhihu.com/p/668951448
https://www.jianshu.com/p/296272f4358f
@EnableAspectJAutoProxy(exposeProxy=true,proxyTargetClass=true)
https://blog.csdn.net/Lwcxz1006/article/details/132111786

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://xiahunao.cn/news/3223734.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

搜维尔科技:OptiTrack在NAB2024展示了一系列业界领先的媒体技术

广泛的显示和动作捕捉跟踪技术组合涵盖无与伦比的室内和室外 LED 解决方案、前沿技术演示以及最新的软件和硬件产品 可视化技术领域的全球领导者 Planar及其附属公司 3D 跟踪系统的全球领导者OptiTrack宣布&#xff0c;两家公司将在 2024 年全国广播协会 (NAB) 展会上展示其最全…

Studying-代码随想录训练营day34| 62.不同路径、63.不同路径II、343.整数拆分、96.不同的二叉搜索树

第34天&#xff0c;动态规划part02&#xff0c;牢记五部曲步骤&#xff0c;编程语言&#xff1a;C 目录 62.不同路径 63.不同路径II 343.整数拆分 96.不同的二叉搜索树 总结 62.不同路径 文档讲解&#xff1a;代码随想录不同路径 视频讲解&#xff1a;手撕不同路径 题目…

接口测试(3)

接口自动化 # 获取图片验证码import requestsresponse requests.get(url"http://kdtx-test.itheima.net/api/captchaImage")print(response.status_code) print(response.text) import requestsurl "http://kdtx-test.itheima.net/api/login" header_da…

爱情和友情触动人心

在这个纷繁复杂的娱乐圈里&#xff0c;爱情与友情的故事总是能触动人心&#xff0c;而“于适前女友包场于适新电影”的新闻&#xff0c;无疑为这个充满故事的舞台又添上了一抹温暖的色彩。这不仅仅是一场电影的包场&#xff0c;更是一段超越常规情感的展现&#xff0c;不禁感叹…

网络安全——防御实验

防御实验一 拓扑结构展示&#xff1a; 一、 根据题目&#xff0c;先为办公区做安全策略主要策略有以下几点&#xff1a; 1、书写名称和描述&#xff0c;名称和描述要明确&#xff0c;让除本人以外的人也能理解 2、确定源地址为办公区&#xff0c;目标地址为DMZ区 3、确定时间…

Xilinx zc706 USB电路解析

作者 QQ群&#xff1a;852283276 微信&#xff1a;arm80x86 微信公众号&#xff1a;青儿创客基地 B站&#xff1a;主页 https://space.bilibili.com/208826118 参考 USB OTG检测原理 USB3320 USB_ID为低电平时候&#xff0c;为host模式&#xff0c;USB_ID为悬空&#xff08;高…

锅总反驳李彦宏说的“不要卷模型,要卷应用”

李彦宏的观点是大家不要卷模型&#xff0c;要卷应用&#xff0c;但我认为这种看法是荒谬的。以下是24条反驳李彦宏观点的论点和论据&#xff1a; 模型的准确性直接决定应用的质量和用户体验&#xff1a; 论据&#xff1a;在自然语言处理、计算机视觉等领域&#xff0c;模型的准…

哦华为仓颉语言

本来我不太想说的&#xff0c;奈何有不少粉丝提问提到了这语言&#xff0c;目前的情况我不透露太多&#xff0c;看过这课程C实现一门计算机编程语言到手撸虚拟机实战的懂的自然懂。 在互联网领域几乎大部分应用软件运行在X86 LINUX上居多&#xff0c;如果你有问题可以先学习这…

C++入门基础(2)

目录 一、引用: 1、定义&#xff1a; 2、特性&#xff1a; 3、引用的使用&#xff1a; 4、const引用&#xff1a;控制权限 const引用定义: const引用可以接收3种对象&#xff1a; 1、正常对象&#xff1a; 2、临时对象&#xff1a; 3、const对象&#xff1a; 总结&…

怎么转播别人的直播

转播别人的直播&#xff0c;特别是实现无缝的实时转播&#xff0c;可以通过一些平台的功能来实现&#xff0c;比如快手和抖音。下面是一个基本的步骤说明&#xff0c;但请注意&#xff0c;具体操作可能会因平台更新或政策变化而有所不同&#xff1a; 找到想要转播的直播间&…

新品茶叶如何一炮而红?营销高手的实战指南!

茶叶&#xff0c;作为中国传统的饮品&#xff0c;已经深入到了我们的日常生活。 随着生活水平的提升&#xff0c;人们对茶叶的需求也在水涨船高。 面对市场上琳琅满目的新品茶叶&#xff0c;如何让自家品牌脱颖而出&#xff0c;确实是现在头疼的问题。下面给大家分享一些目前…

PLC通信网关有什么功能特点?PLC通信网关工作原理-天拓四方

在工业自动化日益发展的今天&#xff0c;PLC已成为工业控制领域的核心设备。工业自动化与信息化深度融合&#xff0c;PLC的应用日益广泛。PLC通信网关&#xff0c;作为工业物联网的重要组成部分&#xff0c;扮演着连接PLC与云平台的桥梁角色&#xff0c;实现设备间的数据交换与…

Linux多进程和多线程(八)多线程

多线程 线程定义线程与进程线程资源 线程相关命令 pidstat 命令 top 命令ps 命令常见的并发方案 1. 多进程模式2. 多线程模式 创建线程 1. pthread_create() 示例:创建一个线程 2. pthread_exit() 退出线程3. pthread_join() 等待线程结束 示例: 线程分离 创建多个线程 示例 1:…

第二章节:JavaScript 基础

参考:https://blog.csdn.net/2303_78006750/article/details/138022514 **说明:**本章节所介绍的 JavaScript 并不深入,因为在 Web 安全领域不需要对 JavaScript 掌握非常深入,我们只需要能够利用本章节提及到的一些语句对网站进行测试就足够了。 1)JavaScript 简介 01-J…

选择排序(C语言版)

选择排序是一种简单直观的排序算法 算法实现 首先在未排序序列中找到最小&#xff08;大&#xff09;元素&#xff0c;存放到排序序列的起始位置。 再从剩余未排序元素中继续寻找最小&#xff08;大&#xff09;元素&#xff0c;然后放到已排序序列的末尾。 重复第二步&…

MySQL GROUP_CONCAT 函数详解与实战应用

提示&#xff1a;在需要将多个值组合成一个列表时&#xff0c;GROUP_CONCAT() 函数为 MySQL 提供了一种强大的方式来处理数据 文章目录 前言什么是 GROUP_CONCAT()基本语法 示例使用 GROUP_CONCAT()去除重复值排序结果 前言 提示&#xff1a;这里可以添加本文要记录的大概内容…

Jmeter在信息头中设置Bearer与 token 的拼接值

思路&#xff1a;先获取token&#xff0c;将token设置成全局变量&#xff0c;再与Bearer拼接。 第一步&#xff1a;使用提取器将token值提取出来&#xff0c;使用setProperty函数将提取的token值设置成全局变量&#xff0c;在登录请求后面添加BeanShell取样器 或者 BeanShell后…

【work】AI八股-神经网络相关

Deep-Learning-Interview-Book/docs/深度学习.md at master amusi/Deep-Learning-Interview-Book GitHub 网上相关总结&#xff1a; 小菜鸡写一写基础深度学习的问题&#xff08;复制大佬的&#xff0c;自己复习用&#xff09; - 知乎 (zhihu.com) CV面试问题准备持续更新贴 …

昇思25天学习打卡营第21天|LSTM+CRF序列标注

1. 学习内容复盘 概述 序列标注指给定输入序列&#xff0c;给序列中每个Token进行标注标签的过程。序列标注问题通常用于从文本中进行信息抽取&#xff0c;包括分词(Word Segmentation)、词性标注(Position Tagging)、命名实体识别(Named Entity Recognition, NER)等。以命名…

Linux|背景 环境搭建

目录 一、简述Linux发展史 1.1计算机的诞生 1.2操作系统的诞生 1.3Linux操作系统开源 1.4Linux发行版本 二、搭建Linux环境 三、使用shell远程登入到Linux 一、简述Linux发展史 可能大家未听说过Linux&#xff0c;或者只知道它是一个搭配在计算机上的操作系统&#xff0…