Java注解

一、什么是注解

        注解是放在Java源码的类、字段、方法、参数前的一种特殊的“注释”,和普通注释的区别是,普通注释被编译器直接忽略,注解则可以被编译器打包进入Class文件。如下图所示就是lombok中的一些注解。

注解的作用:

        从JVM角度看,注解本身对代码逻辑没有任何影响,如何使用注解完全由工具决定。Java的注解可以分为三类:

        第一类是由编辑器使用的注解,例如@Override(让编辑器检查该方法是正确的实现了覆写);@SuppressWarnings(告诉编译器忽略此处代码产生的警告)等等,这类注解不会被编译进入.class文件,它们在编译后就被编译器扔掉了。

        第二类是由工具处理.class文件使用的注解,比如有些工具会在加载class的时候,对class做动态修改,实现一些特殊的功能。这类注解会被编译进入.class文件,但加载结束后并不会存在于内存中。这类注解只被一些底层库使用,一般我们不必自己处理。

        第三类是程序运行期间能够读取的注解,他们加载之后一直存在于JVM中,也是最常用的注解。

二、定义注解

        定义一个注解时,还可以定义配置参数,配置参数可以包括所有基本类型、String、枚举或上面三种类型和Class的数组。因为配置参数必须是常量,所以,上述限制保证了注解在定义时就已经确定了每个参数的值。注解的配置参数可以有默认值,缺少某个配置参数时将使用默认值(强烈建议写默认值)。如果在定义的时候只写了注解,相当于全部使用默认值。如果参数名名称是value且只有这一个参数,可以省略参数名称,直接在()里面写值就可以。

        Java语言中使用@interface语法来定义注解,格式如下:

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Demo13_Annotation {int min() default 0;int max() default 255;
}

注解的参数类似于无参数方法,可以使用default设置一个默认值(推荐),最常用的参数应当命名为value。

        查看上面代码我们可以发现,在注解的定义中也是用到了一些注解,这些注解就被称为元注解,元注解可以修饰其它注解,Java标准库中已经定义了一些元注解,我们只需要使用即可。

        元注解最常用的有两种,分别是@Target和@Retention。

2.1、@Target

        使用@Target可以定义注解能够被应用于源码的哪些位置:

        1、类或接口:ElementType.TYPE;

        2、字段:ElementType.FIELD;

        3、方法:ElementType.METHOD;

        4、构造方法:ElementType.CONSTRUCTOR;

        5、参数方法:ElementType.PARAMETER。

        例如上图中我们定义了只能被应用于字段Field。如果可以用于多个位置,可以把@Target的参数变为数组。

2.2、@Retention

        使用@Retention可以定义注解的生命周期:

        1、仅编译期:RetentionPolicy.SOURCE;

        2、仅Class文件:RetentionPolicy.CLASS;

        3、运行期:RetentionPolicy.RUNTIME。

        如果@Retention不存在,默认是Class。通常我们定义的都是RUNTIME。

2.3、@Repeatable

        可以定义注解是否可重复,应用不广泛。

2.4、@Inherited

        定义子类是否可以继承父类定义的注解,仅针对@Target(ElementType.TYPE)类型的注解有效。

可以看一下NotBlank的注解:

 综上,定义注解的步骤:

        1、使用@interface定义注解:

public @interface Demo13_Annotation {
}

        2、添加参数、默认值:

public @interface Demo13_Annotation {int min() default 0;int max() default 255;
}

        3、用元注解配置注解:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Demo13_Annotation {int min() default 0;int max() default 255;
}

        其中必须设置 @Target和@Retention,@Retention一般为RUNTIME。

三、处理注解

        Java的注解本身对于代码逻辑没有任何影响,其中SOURCE类型的注解在编译期就被丢掉了;CLASS的注解仅保存在class文件中,不被加载至JVM;RUNTIME类型的注解会被加载至JVM且在运行期可以被程序读取。

        如何使用注解完全由工具决定,这里我们只讨论RUNTIME类型的注解。

        JAVA提供了使用反射API判断注解是否存在和读取注解的方法:

3.1、判断某个注解是否存在

  • Class.isAnnotationPresent(Class):是否存在于Class
  • Field.isAnnotationPresent(Class):是否存在于Field
  • Method.isAnnotationPresent(Class)
  • Constructor.isAnnotationPresent(Class)

例如判断@Report是否存在于Person类:

Person.class.isAnnotationPresent(Report.class);

3.2、利用反射读取注解

  • Class.getAnnotation(Class)
  • Field.getAnnotation(Class)
  • Method.getAnnotation(Class)
  • Constructor.getAnnotation(Class)

例如获取Person定义的@Report注解:

// 获取Person定义的@Report注解:
Report report = Person.class.getAnnotation(Report.class);
int type = report.type();
String level = report.level();

使用反射读取注解有两种方法:

        1、先判断是否存在,如果存在,再读取:

Class cls = Person.class;
if (cls.isAnnotationPresent(Report.class)) {Report report = cls.getAnnotation(Report.class);...
}

        2、直接读取注解,如果注解不存在,返回null:

Class cls = Person.class;
Report report = cls.getAnnotation(Report.class);
if (report != null) {...
}

四、使用注解

4.1、声明一个注解

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Demo13_Annotation {int min() default 0;int max() default 255;
}

4.2、声明一个类使用注解

public class Demo14_UseAnnotation {@Demo13_Annotation(min = 3, max = 25)private String name;@Demo13_Annotation(min = 11, max = 11)private String phone;public Demo14_UseAnnotation(){}public Demo14_UseAnnotation(String name, String phone){this.name = name;this.phone = phone;}}

4.3、定义方法来使用注解

void check(Demo14_UseAnnotation demo14_UseAnnotation) throws IllegalArgumentException, ReflectiveOperationException{for (Field field:demo14_UseAnnotation.getClass().getDeclaredFields()){  // 遍历所有Field(包含私有)field.setAccessible(true);  // 如果是私有,设置访问权限if(field.isAnnotationPresent(Demo13_Annotation.class)){  // 判断注解是否存在Demo13_Annotation demo13_Annotation = field.getAnnotation(Demo13_Annotation.class);  // 如果存在则读取注解 Object value = field.get(demo14_UseAnnotation);  // 通过反射获取该字段值if (value instanceof String){  // 判断是否为StringString s = (String) value;if (s.length() < demo13_Annotation.min() || s.length() > demo13_Annotation.max()){  // 判断是否满足注解的min和maxthrow new IllegalArgumentException("Invalid field: " + field.getName());}}}}}

4.4、调用注解方法

import java.lang.reflect.Field;public class Demo15_HandlingAnnotation{public static void main(String[] args) throws IllegalArgumentException, ReflectiveOperationException {Demo15_HandlingAnnotation demo15_HandlingAnnotation = new Demo15_HandlingAnnotation();Demo14_UseAnnotation demo14_UseAnnotation = new Demo14_UseAnnotation("张三", "12345678910");demo15_HandlingAnnotation.check(demo14_UseAnnotation);  // 调用注解方法}void check(Demo14_UseAnnotation demo14_UseAnnotation) throws IllegalArgumentException, ReflectiveOperationException{for (Field field:demo14_UseAnnotation.getClass().getDeclaredFields()){  // 遍历所有Field(包含私有)field.setAccessible(true);  // 如果是私有,设置访问权限if(field.isAnnotationPresent(Demo13_Annotation.class)){  // 判断注解是否存在Demo13_Annotation demo13_Annotation = field.getAnnotation(Demo13_Annotation.class);  // 如果存在则读取注解 Object value = field.get(demo14_UseAnnotation);  // 通过反射获取该字段值if (value instanceof String){  // 判断是否为StringString s = (String) value;if (s.length() < demo13_Annotation.min() || s.length() > demo13_Annotation.max()){  // 判断是否满足注解的min和maxthrow new IllegalArgumentException("Invalid field: " + field.getName());}}}}}
}

通过上述过程我们就完成了注解的定义和使用。

五、实际应用

        使用注解和AOP完成日志记录功能。

5.1、定义日志注解类

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Log {//模块String value() default "Null";//功能String action() default "Null";}

5.2、定义日志切面

@Aspect
@Component
@Slf4j
public class SystemLogAspect {// 定义切点 @Pointcut// 在注解的位置切入代码@Pointcut("@annotation(com....Log)")public void logPointCut(){}// 切面 配置通知@Before("logPointCut()")public void before(JoinPoint joinPoint){//通过反射获取注解MethodSignature signature = (MethodSignature) joinPoint.getSignature();Method method = signature.getMethod();Log actionLog = method.getAnnotation(Log.class);if(actionLog != null){// 获取注解内容String value = actionLog.value();String action = actionLog.action();// 获取HttpServletRequestHttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();// 获取请求方法String httpMethod = request.getMethod();// 获取请求路径String requestURL = request.getRequestURI();// 获取用户IPString ip = request.getRemoteAddr();if (ip.equals("0:0:0:0:0:0:0:1")){ip = "127.0.0.1";}// 获取浏览器信息String browser = UserAgent.parseUserAgentString(request.getHeader("User-Agent")).getBrowser().getName();// ......其它需求log.info("action = " + action + ", value = " + value + ", httpMethod = " + httpMethod + ", requestURL = " + requestURL + ", ip = " + ip + ", browser = " + browser );}}
}

参考链接:使用注解 - 廖雪峰的官方网站

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ResizeKit.NET 自动更改所有控件和字体大小 -Crack Version

ResizeKit2.NET ---Added support for Microsoft .NET 7.0. 使您的应用程序大小和分辨率独立。 ResizeKit.NET 自动更改所有控件和字体的大小&#xff0c;以便它们可以显示在任何大小的表单上。提供完全控制来自定义调整大小过程。即使用户在运行应用程序时切换表单的大小&…

计算物理专题:傅里叶变换与快速傅里叶变换

计算物理专题&#xff1a;傅里叶变换与快速傅里叶变换 傅里叶变换提供一个全新的角度去观察和描述问题&#xff0c;如在量子力学中&#xff0c;动量与坐标表象之间的变换就是傅里叶变换。傅里叶变换同意可以用在数据处理等领域。1965年&#xff0c;Cooley 和 Tukey 提出了快速傅…

WPF中的Behavior及Behavior在MVVM模式下的应用

WPF中的Behavior及Behavior在MVVM模式下的应用 在WPF中&#xff0c;Behaviors&#xff08;行为&#xff09;是一种可重用的组件&#xff0c;可以附加到任何UI元素上&#xff0c;以添加特定的交互行为或功能。Behaviors可以通过附加属性或附加行为的方式来实现。 Behavior并不…

大厂月入3w+,失业焦虑折磨着我

大家好&#xff0c;这里是程序员晚枫&#xff0c;小红书也叫这个名字。 周末和一位老朋友聚会&#xff0c;聊了聊一个很现实的收入问题&#xff0c;巧合的是&#xff1a;他的焦虑&#xff0c;竟然和月薪5k的我一模一样。 今天给大家分享一下。 1、外人看来&#xff0c;让人羡…

在 Linux 中配置 IPv4 和 IPv6 地址详解

概要 IPv4和IPv6是Internet上常用的两种IP地址协议。在Linux系统中&#xff0c;您可以通过配置网络接口来设置IPv4和IPv6地址。本文将详细介绍如何在Linux中配置IPv4和IPv6地址。 步骤 1&#xff1a;确定网络接口 在开始配置IP地址之前&#xff0c;您需要确定要配置的网络接口…

吴恩达ChatGPT《LangChain for LLM Application Development》笔记

基于 LangChain 的 LLM 应用开发 1. 介绍 现在&#xff0c;使用 Prompt 可以快速开发一个应用程序&#xff0c;但是一个应用程序可能需要多次写Prompt&#xff0c;并对 LLM 的输出结果进行解析。因此&#xff0c;需要编写很多胶水代码。 Harrison Chase 创建的 LangChain 框…

初步认识Java垃圾回收算法

GCRoot指被栈上直接或间接引用的对象&#xff0c;或被本地方法栈直接或间接引用的对象&#xff0c;或被方法区引用的对象。 被引用的对象是不能被删除的。 如果对象跟GCRoot并没有直接或间接相连的关系&#xff0c;那么这些对象就可以被删除了。 标记-清理&#xff1a;将需要删…

基于FPGA的RC滤波器设计实现

目录 简介&#xff1a; 传递函数 FPGA代码实现 总结 简介&#xff1a; RC滤波器的特性基本情况介绍 RC一阶低通滤波介绍&#xff1b;RC滤波器电路简单&#xff0c;抗干扰性强&#xff0c;有较好的低频性能&#xff0c;并且选用标准的阻容元件易得&#xff0c;所以在工程测…

GAMES101 笔记 Lecture08 Shading 2(Shading, Pipeline and Texture Mapping)

目录 Specular Term(高光项)Ambient Term(环境光照项)Blinn-Phong Reflection ModelShading Frequencies(着色频率)Shade each triangle(flat shading)在每个三角形上进行着色Shade each vertex (Gouraud shading)(顶点着色)Shade each pixel (Phong shading)Defining Per-Vert…

ES基本操作(JavaAPI篇)

引入jar包依赖 <dependencies><dependency><groupId>org.elasticsearch</groupId><artifactId>elasticsearch</artifactId><version>7.8.0</version></dependency><!-- es客户端 --><dependency><groupI…

Loadrunner怎么实现MD5加密

目录 前言&#xff1a; 1、写一个md5.h文件&#xff0c;将其放入脚本路径下 2、在globals.h中加入#include “md5.h” 3、在Action中写脚本&#xff0c;脚本示例如下&#xff1a; 前言&#xff1a; 在 LoadRunner 中实现 MD5 加密可以通过使用 LoadRunner 提供的函数来完成…

【FPGA零基础学习之旅#9】状态机基础知识

&#x1f389;欢迎来到FPGA专栏~状态机基础知识 ☆* o(≧▽≦)o *☆嗨~我是小夏与酒&#x1f379; ✨博客主页&#xff1a;小夏与酒的博客 &#x1f388;该系列文章专栏&#xff1a;FPGA学习之旅 文章作者技术和水平有限&#xff0c;如果文中出现错误&#xff0c;希望大家能指正…