java注解是怎么实现的,java注解编程
背景为什么要再次梳理java注释?很明显,因为它很重要。也为研究各种开源框架做了铺垫。只有搞清楚Java注释的相关原理,才能理解大多数框架的底层设计。
原点注记也称为元数据,是1.5版中引入的一项功能。它用于标记和解释代码,可以注释包、类、接口、字段、方法参数、局部变量等。它不包含任何业务逻辑。
一般来说,有三种注释:
JDK自己的相关标注、自定义标注、第三方标注(比如相关框架中的标注)有三个步骤:定义、配置、解析。
定义:定义标签配置:将标签键入需要使用的代码中;解析:在编译器或运行时检测标签,并对元注释执行特殊操作。什么是元注释?注释的作用是注释其他的注释。有五种注释:
@Retention:指定其修改的注释的保留策略@Document:该注释为标记注释,用于表示注释将被记录@Target:用于限制注释的应用范围@Inherited:该注释使父类的注释能够被其子类继承@Repeatable:该注释是Java8中的新注释,用于开发重复注释@Retention注释用于指定修改后的注释可以保留多长时间,即指定JVM策略。
目前,有三种策略
JVM可以在保留的运行时获得注释信息(反射)。运行时,这是最长的注释持续时间。@Document annotation @ Document annotation用于指定可以通过javadoc工具将修饰的注释提取到@ Document中。如果在定义注解类时用@Document注解来修饰它,那么用注解修饰的程序元素的所有API文档都将包含注解描述。
@ Target annotation @ Target annotation用于限制注释的范围,即指定修饰的注释可以用于哪些程序单元。标注方法如下:@Target({应用类型1,应用类型2,}) [@ target (elementtype.field)]
枚举值介绍如下:
@ Inherited annotation @ Inherited annotation指定批注是继承的。如果注释是用@Inherited修饰的,那么当类使用注释时,它的子类会自动被修饰。
按照上面的三步流程,我们举一个写代码的例子来说明:
(1)定义注释
@Target(ElementType。类型)
@保留(RetentionPolicy。运行时间)
@继承
public @interface继承扩展{
字符串注释();
int order()默认为1;
}(2)配置:标记类。
@ inherited extend(comment= comment inheritance ,order=2)
公共基类{
}(3)分析:获取评论,分析测试。
公共类InheritedDemo扩展基{
公共静态void main(String[] args) {
//从此类获取父类的注释信息
inherited extend extend=inherited demo . class . get annotation(inherited extend . class);
//输出InheritedExtend批注成员信息
system . out . println(extend . comment(): extend . order());
//打印出InheritedDemo类是否有@InheritedExtend修饰。
system . out . println(inherited demo . class . isanotationpresent(inherited extend . class));
}
}结果输出:
评论继承性:2 true的上述结果很好地说明了这个评论的继承性。
@ Repeatable comment @ Repeatable comment是Java8中的新注释,用于开发重复性注释。在Java8之前,同一个程序元素之前只能使用一个同类型的注释。如果需要在同一个元素之前使用多个同类型的标注,就必须通过标注容器来实现。从Java8开始,允许使用多个同类型的注释来修饰同一个元素,前提是同类型的注释是可重复的,即在定义注释时要用@Repeatable元注释来修饰。
(1)定义注释
@Target(ElementType。类型)
@保留(RetentionPolicy。运行时间)
@ Repeatable(annol contents . class)
public @ interface RepeatableAnnol {
String()默认为“老猫”;
int age();
}
//注释为容器,
@Target(ElementType。类型)
@保留(RetentionPolicy。运行时间)
@界面公告内容{
//定义value成员变量,该变量可以接受多个@ Repeatableinnol批注。
repeatable annol[]value();
}(2)注解使用以及解析
@RepeatableAnnol(name=张三,年龄=12岁)
@RepeatableAnnol(年龄=23岁)
公共类RepeatableAnnolDemo {
公共静态void main(String[] args) {
RepeatableAnnol[]repeatableAnnols=repeatableannoldemo。班级。getdeclaredannotationsbytype(RepeatableAnnol。类);
for(RepeatableAnnol RepeatableAnnol:repeatableAnnols){
系统。出去。println(可重复的annol。name()-可重复的annol。年龄());
}
annol contents annol contents=repeatableannoldemo。班级。getdeclaredannotation(annol内容。类);
系统。出去。println(annol内容);
}
}结果输出:
张三- 12
老猫- 23
@ com。卡帕头爸爸。注释。可重复。annol内容(value={ @ com。卡帕头爸爸。注释。可重复。可重复的annol(名称=张三,年龄=12),@ com。卡帕头爸爸。注释。可重复。可重复的annol(名称=老猫,年龄=23)})自定义注解实战应用利用注解解析实现系统日志记录,主要用于记录相关的日志到数据库,当然,老猫这里的演示只会到日志打印层面,至于数据库落库存储有兴趣的小伙伴可以进行扩展。
以下是专家依赖:
属国
属国
groupId组织。spring框架。boot/groupId
项目的名称弹簧启动启动器/artifactId
/依赖关系
属国
groupId组织。spring框架。boot/groupId
工件id spring-boot-starter-web/工件id
/依赖关系
属国
groupId组织。spring框架。boot/groupId
artifactId spring-boot-starter-AOP/artifactId
/依赖关系
属国
groupId com.alibaba /groupId
artifactId fastjson /artifactId
版本1 .2 .79/版本
/依赖关系
/依赖关系注解的定义如下:
@Target(ElementType .方法)
@保留(保留政策.运行时间)
@已记录
公共@接口操作日志{
字符串desc()默认为"";
}这个地方只是定义了一个字段,当然大家也可以进行拓展。
接下来,咱们以这个注解作为切点编写相关的切面程序。具体代码如下:
@Aspect
@组件
@Order(0)
公共类操作设备{
@ Pointcut( @ annotation(com。卡帕头爸爸。注释。操作日志)’)
公共void recordLog(){
}
@Around(recordLog())
公共对象recordLogOne(过程连接点连接点)抛出可投掷的
System.out.println(进来了);
方法签名方法签名=(方法签名)连接点。获取签名();
方法方法=方法签名。get方法();
操作日志操作日志=方法签名。获取方法().获取注释(操作日志。类);
string spELString=操作日志。desc();
//创建解析器
SpelExpressionParser parser=new SpelExpressionParser();
//获取表达式
表达式表达式=解析器。解析表达式(spELString);
//设置解析上下文(有哪些占位符,以及每种占位符的值)
EvaluationContext context=new standardyevaluationcontext();
//获取参数值
object[]args=连接点。get args();
//获取运行时参数的名称
DefaultParameterNameDiscoverer discoverer=new DefaultParameterNameDiscoverer();
string[]参数名称=discoverer。获取参数名(方法);
for(int I=0;我参数名字。长度;i ) {
语境。set变量(参数名[I],args[I]);
}
//解析,获取替换后的结果
字符串结果=表达式。getvalue(上下文)。toString();
System.out.println(结果);
返回连接点。proceed();
}
}关于切面这块不多做赘述,非本篇文章的重点。
接下来就可以在我们的代码层面使用相关的注解了,具体如下:
@服务
公共类对象实现用户服务{
@OperateLog(desc=#user.desc )
公共void saveUser(用户用户){
System.out.println(测试注解.);
}
}关于控制器层面就省略了,都是比较简单的。
通过上述切面以及注解解析,我们可以获取每次传参的参数内容,并且将相关的日志进行记录下来,当然这里面涉及到了表达式语言表达式注入,相关的知识点,小伙伴们可以自行学习。
最终启动服务,并且请求之后具体的日志如下。
进来了
这是测试
测试注解.
{age:12, desc :这是一个测试, id:1, name :张三, operator: operator}至此,关于Java注释的回顾性学习已经结束,接下来我们再来看一些底层代码可能就容易多了。
版权归作者所有:原创作品来自博主小二上九8,转载请联系作者取得转载授权,否则将追究法律责任。
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。