背景

为什么要再次梳理一下java注解,显而易见,因为重要啊。也是为研究各大类开源框架做铺垫,只有弄清楚Java注解相关原理,才能看懂大部分框架底层的设计。

缘起

注解也叫做元数据,是JDK1.5版本开始引入的一个特性,用来对代码进行标记说明,可以对包、类、接口、字段、方法参数、局部变量等进行注解修饰。其本身不包含任何业务逻辑。

一般注解大类分为三种:

  • JDK自带的相关注解
  • 自定义的注解
  • 第三方的(例如相关的框架中的注解)

注解三步走:定义、配置、解析

  • 定义:定义标记
  • 配置:把标记打到需要用到的代码中
  • 解析:在编译器或运行时检测到标记,并进行特殊操作

元注解

什么是元注解?元注解的作用就是负责注解其他注解。元注解有以下五种:

  • @Retention:指定其所修饰的注解的保留策略
  • @Document:该注解是一个标记注解,用于指示一个注解将被文档化
  • @Target:用来限制注解的使用范围
  • @Inherited:该注解使父类的注解能被其子类继承
  • @Repeatable:该注解是Java8新增的注解,用于开发重复注解

@Retention注解

用于指定被修饰的注解可以保留多长时间,即指定JVM策略在哪个时间点上删除当前注解。

目前存在以下三种策略

策略值 功能描述
Retention.SOURCE 注解只在源文件中保留,在编译期间删除
Retention.CLASS 注解只在编译期间存在于.class文件中,运行时JVM不可获取注解信息,该策略值也是默认值
Retention.RUNTIME 运行时JVM可以获取注解信息(反射),是最长注解持续期

@Document注解

@Document注解用于指定被修饰的注解可以被javadoc工具提取成文档。定义注解类时使用@Document注解进行修饰,则所有使用该注解修饰的程序元素的API文档中将会包含该注解说明。

@Target注解

@Target注解用来限制注解的使用范围,即指定被修饰的注解能用于哪些程序单元。标记注解方式如下:@Target({应用类型1, 应用类型2,...})【@Target(ElementType.FIELD)】

枚举值的介绍如下:

枚举值 功能描述
ElementType.Type 可以修饰类、接口、注解或枚举类型
ElementType.FIELD 可以修饰属性(成员变量),包括枚举常量
ElementType.METHOD 可以修饰方法
ElementType.PAPAMETER 可以修饰参数
ElementType.CONSTRUCTOR 可以修饰构造方法
ElementType.LOCAL_VARIABLE 可以修饰局部变量
ElementType.ANNOTATION_TYPE 可以修饰注解类
ElementType.PACKAGE 可以修饰包
ElementType.TYPE_PARAMETER JDK8之后的新特性,表示该注解能写在类型变量的声明语句中(如,泛型声明)
ElementType.TYPE_USE JDK8之后的新特性,表示该注解能写在使用类型的任何语句中(例如:声明语句、泛型和强制转换语句中的类型)

@Inherited注解

@Inherited注解指定注解具有继承性,如果某个注解使用@Inherited进行修饰,则该类使用该注解时,其子类将自动被修饰。

按照以上三步走的流程,咱们这里来举例子写代码说明一下:

(1)定义注解

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface InheritedExtend {
String comment();
int order() default 1;
}

(2)配置:标记打到类上

@InheritedExtend(comment ="注解继承",order = 2)
public class Base {
}

(3)解析:获取注解并解析做测试

public class InheritedDemo extends Base{
public static void main(String[] args) {
//从本类中获取父类注解信息
InheritedExtend extend = InheritedDemo.class.getAnnotation(InheritedExtend.class);
//输出InheritedExtend注解成员信息
System.out.println(extend.comment()+":"+extend.order());
//打印出InheritedDemo是否类是否具有@InheritedExtend修饰
System.out.println(InheritedDemo.class.isAnnotationPresent(InheritedExtend.class));
}
}

结果输出:

注解继承:2 true

以上结果就很好地说明了该注解的继承性质。

@Repeatable注解

@Repeatable注解是Java8新增的注解,用于开发重复注解。在Java8之前,同一个程序元素前只能使用一个相同类型的注解,如果需要在同一个元素前使用多个相同类型的注解必须通过注解容器来实现。从Java8开始,允许使用多个相同的类型注解来修饰同一个元素,前提是该类型的注解是可重复的,即在定义注解时要用 @Repeatable元注解进行修饰。

(1)定义注解

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(AnnolContents.class)
public @interface RepeatableAnnol {
String name() default "老猫";
int age();
} //注解为容器,
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface AnnolContents {
//定义value成员变量,该成员变量可以接受多个@RepeatableAnnol注解
RepeatableAnnol[] value();
}

(2)注解使用以及解析

@RepeatableAnnol(name = "张三",age = 12)
@RepeatableAnnol(age = 23)
public class RepeatableAnnolDemo {
public static void main(String[] args) {
RepeatableAnnol[] repeatableAnnols = RepeatableAnnolDemo.class.getDeclaredAnnotationsByType(RepeatableAnnol.class); for(RepeatableAnnol repeatableAnnol : repeatableAnnols){
System.out.println(repeatableAnnol.name() + "----->" + repeatableAnnol.age());
} AnnolContents annolContents = RepeatableAnnolDemo.class.getDeclaredAnnotation(AnnolContents.class);
System.out.println(annolContents);
}
}

结果输出:

张三----->12
老猫----->23
@com.ktdaddy.annotation.repeatable.AnnolContents(value={@com.ktdaddy.annotation.repeatable.RepeatableAnnol(name="张三", age=12), @com.ktdaddy.annotation.repeatable.RepeatableAnnol(name="老猫", age=23)})

自定义注解实战应用

利用注解+springAOP实现系统日志记录,主要用于记录相关的日志到数据库,当然,老猫这里的demo只会到日志打印层面,至于数据库落库存储有兴趣的小伙伴可以进行扩展。

以下是maven依赖:

 <dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency> <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>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.79</version>
</dependency>
</dependencies>

注解的定义如下:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface OperateLog {
String desc() default "";
}

这个地方只是定义了一个字段,当然大家也可以进行拓展。

接下来,咱们以这个注解作为切点编写相关的切面程序。具体代码如下:

@Aspect
@Component
@Order(0)
public class OperateLogAdvice { @Pointcut("@annotation(com.ktdaddy.annotation.OperateLog)")
public void recordLog(){
} @Around("recordLog()")
public Object recordLogOne(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("进来了");
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
Method method = methodSignature.getMethod();
OperateLog operateLog = methodSignature.getMethod().getAnnotation(OperateLog.class);
String spELString = operateLog.desc();
//创建解析器
SpelExpressionParser parser = new SpelExpressionParser();
//获取表达式
Expression expression = parser.parseExpression(spELString);
//设置解析上下文(有哪些占位符,以及每种占位符的值)
EvaluationContext context = new StandardEvaluationContext();
//获取参数值
Object[] args = joinPoint.getArgs();
//获取运行时参数的名称
DefaultParameterNameDiscoverer discoverer = new DefaultParameterNameDiscoverer();
String[] parameterNames = discoverer.getParameterNames(method);
for (int i = 0; i < parameterNames.length; i++) {
context.setVariable(parameterNames[i],args[i]);
}
//解析,获取替换后的结果
String result = expression.getValue(context).toString();
System.out.println(result);
return joinPoint.proceed();
}
}

关于切面这块不多做赘述,非本篇文章的重点。

接下来就可以在我们的代码层面使用相关的注解了,具体如下:

@Service
public class UserServiceImpl implements UserService {
@OperateLog(desc = "#user.desc")
public void saveUser(User user){
System.out.println("测试注解...");
}
}

关于controller层面就省略了,都是比较简单的。

通过上述切面以及注解解析,我们可以获取每次传参的参数内容,并且将相关的日志进行记录下来,当然这里面涉及到了SpEL表达式注入,相关的知识点,小伙伴们可以自行学习。

最终启动服务,并且请求之后具体的日志如下。

进来了
这是测试
测试注解...
{"age":12,"desc":"这是测试","id":1,"name":"张三","operator":"操作人"}

至此关于Java注解的回顾学习已经结束,之后咱们再去看一些底层代码的时候或许会轻松很多。

Java注解系统学习与实战的更多相关文章

  1. Java注解Annotation学习

    学习注解Annotation的原理,这篇讲的不错:http://blog.csdn.net/lylwo317/article/details/52163304 先自定义一个运行时注解 @Target( ...

  2. Java注解Annotation学习笔记

    一.自定义注解 1. 使用关键字 @interface 2. 默认情况下,注解可以修饰 类.方法.接口等. 3. 如下为一个基本的注解例子: //注解中的成员变量非常像定义接口 public @int ...

  3. Java 注解(Annoation)学习笔记

    1 Junit中的@Test为例: 1.1 用注解(@Test)前 private boolean isTestMethod(Method m) { return m.getParameterType ...

  4. Java注解简单学习

    注解(也被称作元数据)为我们在代码中添加信息提供了一种形式化的方法,使我们在稍后某个时刻可以很方便的使用这些数据,其在一定程度上将元数据与源代码文件结合在一起,而不是保存在外部文档中. 注解使我们可以 ...

  5. 深入学习JAVA注解-Annotation(学习过程)

    JAVA注解-Annotation学习 本文目的:项目开发过程中遇到自定义注解,想要弄清楚其原理,但是自己的基础知识不足以支撑自己去探索此问题,所以先记录问题,然后补充基础知识,然后解决其问题.记录此 ...

  6. Java注解最全详解(超级详细)

    Java注解是一个很重要的知识点,掌握好Java注解有利于学习Java开发框架底层实现.@mikechen Java注解定义 Java注解又称Java标注,是在 JDK5 时引入的新特性,注解(也被称 ...

  7. 一文看懂java io系统 (转)

    出处:  一文看懂java io系统   学习java IO系统,重点是学会IO模型,了解了各种IO模型之后就可以更好的理解java IO Java IO 是一套Java用来读写数据(输入和输出)的A ...

  8. 深入JAVA注解-Annotation(学习过程)

    JAVA注解-Annotation学习 本文目的:项目开发过程中遇到自定义注解,想要弄清楚其原理,但是自己的基础知识不足以支撑自己去探索此问题,所以先记录问题,然后补充基础知识,然后解决其问题.记录此 ...

  9. Java注解(二):实战 - 直接使用对象列表生成报表

    通过对Java注解(一):介绍,思想及优点学习了解,相信大家对Java注解有一定程度的了解,本篇文章将实战项目中的应用来加深对Java注解的了解. 本实例实现根据指定字段的JavaBean,生成对应列 ...

随机推荐

  1. 【python基础】第11回 数据类型内置方法 02

    本章内容概要 列表内置方法 字典内置方法 元组内置方法 集合内置方法 可变类型与不可变类型 本章内容详细 1.列表内置方法 list 列表在调用内置方法之后不会产生新的值 1.1 统计列表中的数据值的 ...

  2. nginx配置的server_name无法访问

    问题: 我的nginx.conf配置文件中的server_name是这样子的,然后无法访问. 但是如果说server_name后面改成服务器的IP地址却是可以访问的. 解决方案: 在本机上(不是服务器 ...

  3. Linux for CentOS 下的 keepalived 安装与卸载以及相关命令操作之详细教程

    百度百科解释: keepalived 是一个类似于 layer3, 4 & 7 交换机制的软件,也就是我们平时说的第 3 层.第 4 层和第 7 层交换.Keepalived 的作用是检测 w ...

  4. NC200190 矩阵消除游戏

    NC200190 矩阵消除游戏 题目 题目描述 牛妹在玩一个名为矩阵消除的游戏,矩阵的大小是 \({n}\) 行 \({m}\) 列,第 \({i}\) 行第 \({j}\) 列的单元格的权值为 \( ...

  5. 使用ventoy制作启动盘

    先去应用商店下载,非Deepin用户去官网下载Download.Ventoy. 先确认一下自己的系统镜像是否在清单内.(其实不在也没事) 按照使用说明操作Get start.Ventoy,建议配置为G ...

  6. [ERROR] Another process with pid 914 is using unix socket file.

    mysql启动报错 1.首先到mysql的配置文件中,确定socket文件路径 vim /etc/my.cnf 2.删除mysql.sock.lock 3.启动mysql

  7. windows10:vscode下go语言的适配

    ps:本篇依赖golang的sdk已经安装完成: url:https://www.cnblogs.com/mrwhite2020/p/16475731.html   一.下载vscode,选择wind ...

  8. IM系统-消息流化一些常见问题

    原创不易,求分享.求一键三连 之前说过IM系统的一些优化,但是在网络上传输数据对于数据的流化和反流化也是处理异常情况的重点环节,不处理好可能会出现一些消息发送成功,但是解析失败的情况,本文就带大家来一 ...

  9. 并发刺客(False Sharing)——并发程序的隐藏杀手

    并发刺客(False Sharing)--并发程序的隐藏杀手 前言 前段时间在各种社交平台"雪糕刺客"这个词比较火,简单的来说就是雪糕的价格非常高!其实在并发程序当中也有一个刺客, ...

  10. 学习与尝试 --> 事件风暴

    事件风暴 1. 基础概念 术语 执行者 -----> 是指执行的角色,系统的主体,是导致系统状态变化的触发源 人员,系统的用户,操作人员等 系统,系统本身执行的,或者调度的,自动触发的 ,第三方 ...