Java注解系统学习与实战
背景
为什么要再次梳理一下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注解系统学习与实战的更多相关文章
- Java注解Annotation学习
学习注解Annotation的原理,这篇讲的不错:http://blog.csdn.net/lylwo317/article/details/52163304 先自定义一个运行时注解 @Target( ...
- Java注解Annotation学习笔记
一.自定义注解 1. 使用关键字 @interface 2. 默认情况下,注解可以修饰 类.方法.接口等. 3. 如下为一个基本的注解例子: //注解中的成员变量非常像定义接口 public @int ...
- Java 注解(Annoation)学习笔记
1 Junit中的@Test为例: 1.1 用注解(@Test)前 private boolean isTestMethod(Method m) { return m.getParameterType ...
- Java注解简单学习
注解(也被称作元数据)为我们在代码中添加信息提供了一种形式化的方法,使我们在稍后某个时刻可以很方便的使用这些数据,其在一定程度上将元数据与源代码文件结合在一起,而不是保存在外部文档中. 注解使我们可以 ...
- 深入学习JAVA注解-Annotation(学习过程)
JAVA注解-Annotation学习 本文目的:项目开发过程中遇到自定义注解,想要弄清楚其原理,但是自己的基础知识不足以支撑自己去探索此问题,所以先记录问题,然后补充基础知识,然后解决其问题.记录此 ...
- Java注解最全详解(超级详细)
Java注解是一个很重要的知识点,掌握好Java注解有利于学习Java开发框架底层实现.@mikechen Java注解定义 Java注解又称Java标注,是在 JDK5 时引入的新特性,注解(也被称 ...
- 一文看懂java io系统 (转)
出处: 一文看懂java io系统 学习java IO系统,重点是学会IO模型,了解了各种IO模型之后就可以更好的理解java IO Java IO 是一套Java用来读写数据(输入和输出)的A ...
- 深入JAVA注解-Annotation(学习过程)
JAVA注解-Annotation学习 本文目的:项目开发过程中遇到自定义注解,想要弄清楚其原理,但是自己的基础知识不足以支撑自己去探索此问题,所以先记录问题,然后补充基础知识,然后解决其问题.记录此 ...
- Java注解(二):实战 - 直接使用对象列表生成报表
通过对Java注解(一):介绍,思想及优点学习了解,相信大家对Java注解有一定程度的了解,本篇文章将实战项目中的应用来加深对Java注解的了解. 本实例实现根据指定字段的JavaBean,生成对应列 ...
随机推荐
- 再测云原生数据库性能:PolarDB依旧最强,TDSQL-C、GaussDB变化不大
1.摘要 近期,腾讯云数据库在文章「腾讯云TDSQL-C重磅升级,性能全面领跑云原生数据库市场」中提到,某些场景下性能有非常大的提升,且超过国内某橙色云厂商.恰好,在5月份,我们对各个厂商的云原生数据 ...
- 自建批量更改标准BO数据程序
by zyi
- NC204859 组队
NC204859 组队 题目 题目描述 你的团队中有 \(n\) 个人,每个人有一个能力值 \(a_i\),现在需要选择若干个人组成一个团队去参加比赛,由于比赛的规则限制,一个团队里面任意两个人能力的 ...
- 自定义 systemd service
Red Hat Linux 自 7 版本后 采用systemd 形式取代原先 init ,用户可以参考 系统service 创建自己的service ,以便于日常统一管理,系统service 存储路径 ...
- Cascade-LSTM: A Tree-Structured Neural Classifier for Detecting Misinformation Cascades(KDD20)
Cascade-LSTM是一个用于虚假信息级联检测的树结构神经分类器,它本质上是一个谣言(假新闻)检测模型,它将谣言检测任务视为一个树分类问题. Cascade-LSTM在递归神经网络(本文具体基于T ...
- 【C++】学生管理系统
[C++]学生管理系统 一道非常经典的C语言题目,用C++实现 题目如下: 输入功能:由键盘输入10个学生的学号.姓名.三科成绩,并计算出平均成绩和总成绩,然后将它存入文件stud.dat. 插入 ...
- Maven3 入门到入门
Maven3 Core Overview Maven是一个项目管理工具,它包含了一个项目对象模型(Project Object Model,POM) ,一组标准集合,一个项目生命周期(Project ...
- 如何优化API?8个实用技巧!【eolink翻译】
使用 API 可以让公司利用现代连接的力量来帮助他们扩大全球影响力.传输数据和改进集成.由于 API 使企业能够简化流程并增强可用性,所以企业会使用一些优化策略,不断优化流程,比如接下来要说到的8个技 ...
- super详解(继承)
//在Java中,所有的类,都默认直接或者间接继承objec类// Person 人 :父类public class Person /*extends object*/ { public Person ...
- 强化版按键消抖Verilog实现
介绍:按键的物理结构导致了会有抖动现象的出现,判断按键是否真正按下,需要把抖动的部分滤波.根据经验可知,抖动一般在20ms内,所以常规的消抖方法是从变化沿出现时刻开始,延时20ms后判断按键的状态.这 ...