Java高级特性——Java注解介绍与底层实现原理(下),Java开发入门与实战

String value() default “我是定义在方法上的注解元素value的默认值”;

}

MyAnnotationField.java

/**

  • @author Coder编程

  • @Title: MyAnnotationField

  • @ProjectName simple-framework

  • @Description: 定义一个可以注解在FIELD上的注解

  • @date 2021/4/22 14:17

*/

@Target({ElementType.FIELD})

@Retention(RetentionPolicy.RUNTIME)

public @interface MyAnnotationField {

/**

  • 定义注解的一个元素 并给定默认值

  • @return

*/

String value() default “我是定义在字段上的注解元素value的默认值”;

/**

  • 定义注解一个type类型

  • @return

*/

int type();

}

MyAnnotationParameter.java

/**

  • @author Coder编程

  • @Title: MyAnnotationParameter

  • @ProjectName simple-framework

  • @Description: TODO

  • @date 2021/4/22 14:20

*/

@Target({ElementType.PARAMETER})

@Retention(RetentionPolicy.RUNTIME)

public @interface MyAnnotationParameter {

/**

  • 定义注解的一个元素 并给定默认值

  • @return

*/

String value() default “我是定义在参数上的注解元素value的默认值”;

/**

  • 定义注解类型

  • @return

*/

int type() default 1;

}

测试

package demo.annotation;

import java.lang.annotation.Annotation;

import java.lang.reflect.Field;

import java.lang.reflect.Method;

/**

  • @author Coder编程

  • @Title: TestAnnotation

  • @ProjectName simple-framework

  • @Description: TODO

  • @date 2021/4/22 14:20

*/

@MyAnnotationType

public class TestAnnotation {

@MyAnnotationField(type = 1)

private String field = “我是字段”;

@MyAnnotationMethod(“测试方法”)

public void methodTest(@MyAnnotationParameter(“测试参数”)String str){

System.out.println("我是测试方法,Str = " + str);

}

/**

  • 获取类上的注解

  • @throws ClassNotFoundException

*/

public static void parseTypeAnnotation() throws ClassNotFoundException {

//获取类上的注解

MyAnnotationType annotation = TestAnnotation.class.getAnnotation(MyAnnotationType.class);

System.out.println(“类上注解”+annotation);

//另外一种方法

Class clazz = Class.forName(“demo.annotation.TestAnnotation”);

/获取到类上的所有注解,只在类上,不包括成员,方法上的注解/

Annotation[] annotations = clazz.getAnnotations();

for (Annotation annotation1 : annotations) {

MyAnnotationType myAnnotationType = (MyAnnotationType)annotation1;

System.out.println(myAnnotationType.value());

}

}

/**

  • 获取字段上的注解

  • @throws ClassNotFoundException

*/

public static void parseFieldAnnotation() throws ClassNotFoundException {

Class clazz = Class.forName(“demo.annotation.TestAnnotation”);

Field[] declaredFields = clazz.getDeclaredFields();

for (Field declaredField : declaredFields) {

//判断成员变量中是否有指定注解类型的注解

boolean hasAnnotation = declaredField.isAnnotationPresent(MyAnnotationField.class);

if(hasAnnotation){

MyAnnotationField annotation = declaredField.getAnnotation(MyAnnotationField.class);

System.out.println(annotation.value());

}

}

}

/**

  • 获取方法,参数上的注解

  • @throws ClassNotFoundException

  • @throws NoSuchMethodException

*/

public static void parseMethodAnnotation() throws ClassNotFoundException, NoSuchMethodException {

Class clazz = Class.forName(“demo.annotation.TestAnnotation”);

Method methodTest = clazz.getDeclaredMethod(“methodTest”, String.class);

MyAnnotationMethod annotation = methodTest.getAnnotation(MyAnnotationMethod.class);

System.out.println(annotation.value());

Annotation[][] parameterAnnotations = methodTest.getParameterAnnotations();

for (Annotation[] parameterAnnotation : parameterAnnotations) {

for (Annotation annotation1 : parameterAnnotation) {

if(annotation1 instanceof MyAnnotationParameter){

System.out.println(“方法参数上的注解:”+((MyAnnotationParameter) annotation1).value());

}

}

}

}

public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {

//parseTypeAnnotation();

parseFieldAnnotation();

//parseMethodAnnotation();

}

}

大家可以将以上代码多多动手,实战敲一下,加深印象!上面有涉及到 Java的反射内容,我的文章中好像还没写过,下次补上。不太了解的同学也可以自行查询下。

注解实现原理


通过第一篇文章,我们知道了什么是注解,通过上面的自定义注解练习,我们又加深了对注解的印象。可是好像总是感觉少了点什么?注解的工作原理是什么?它是怎么获取到属性的值?注解的本质到底是什么?是接口呢?还是抽象类呢?

我们下来看下我们MyAnnotationType的类关系图。

IDEA工具图1

IDEA工具图2

可以发现MyAnnotationType底层是继承了Annotation接口。当然,我们除了用这种方法,还可以通过查看字节码的方式来查看MyAnnotationType 底层到底是啥。

这里我们的命令是:javap -v MyAnnotation.class 注:-v 是 -verbose 缩写

$ javap -v MyAnnotationType.class

Classfile /E:/Github/simple-framework/target/classes/demo/annotation/MyAnnotationType.class

Last modified 2021-4-23; size 530 bytes

MD5 checksum 142d541c8ff55e25c20881f705f9d876

Compiled from “MyAnnotationType.java”

public interface demo.annotation.MyAnnotationType extends java.lang.annotation.Annotation

minor version: 0

major version: 52

flags: ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION

Constant pool:

#1 = Class #17 // demo/annotation/MyAnnotationType

#2 = Class #18 // java/lang/Object

#3 = Class #19 // java/lang/annotation/Annotation

#4 = Utf8 value

#5 = Utf8 ()Ljava/lang/String;

#6 = Utf8 AnnotationDefault

#7 = Utf8 我是定义在类接口枚举类上的注解元素value的默认值

#8 = Utf8 SourceFile

#9 = Utf8 MyAnnotationType.java

#10 = Utf8 RuntimeVisibleAnnotations

#11 = Utf8 Ljava/lang/annotation/Target;

#12 = Utf8 Ljava/lang/annotation/ElementType;

#13 = Utf8 TYPE

#14 = Utf8 Ljava/lang/annotation/Retention;

#15 = Utf8 Ljava/lang/annotation/RetentionPolicy;

#16 = Utf8 RUNTIME

#17 = Utf8 demo/annotation/MyAnnotationType

#18 = Utf8 java/lang/Object

#19 = Utf8 java/lang/annotation/Annotation

{

public abstract java.lang.String value();

descriptor: ()Ljava/lang/String;

flags: ACC_PUBLIC, ACC_ABSTRACT

AnnotationDefault:

default_value: s#7}

SourceFile: “MyAnnotationType.java”

RuntimeVisibleAnnotations:

0: #11(#4=[e#12.#13])

1: #14(#4=e#15.#16)

很明显,中间写了public interface demo.annotation.MyAnnotationType extends java.lang.annotation.Annotation 因此,可以看出注解的本质是 继承Annotation接口

接下来我们继续探索。

我们用IDEA断点来调试下我们 parseTypeAnnotation()方法

断点调试

为了能更清晰的看到代码中间运行的过程。我们需要在IDEA 启动的时候加入一些参数:

-Djdk.proxy.ProxyGenerator.saveGeneratedFiles=true//记录JDK动态代理生成器,将生成代理类的文件保留下来

VM ptions中写入

设置参数

运行完程序后,我们会得到一些文件

生成文件

我们直接通过IDEA打开 $proxy1 文件。

//

// Source code recreated from a .class file by IntelliJ IDEA

// (powered by FernFlower decompiler)

//

package com.sun.proxy;

import demo.annotation.MyAnnotationType;

import java.lang.reflect.InvocationHandler;

import java.lang.reflect.Method;

import java.lang.reflect.Proxy;

import java.lang.reflect.UndeclaredThrowableException;

public final class $Proxy1 extends Proxy implements MyAnnotationType {

private static Method m1;

private static Method m2;

private static Method m4;

private static Method m0;

private static Method m3;

public $Proxy1(InvocationHandler var1) throws {

super(var1);

}

public final boolean equals(Object var1) throws {

try {

return (Boolean)super.h.invoke(this, m1, new Object[]{var1});

} catch (RuntimeException | Error var3) {

throw var3;

} catch (Throwable var4) {

throw new UndeclaredThrowableException(var4);

}

}

public final String toString() throws {

try {

return (String)super.h.invoke(this, m2, (Object[])null);

} catch (RuntimeException | Error var2) {

throw var2;

} catch (Throwable var3) {

throw new UndeclaredThrowableException(var3);

}

}

public final Class annotationType() throws {

try {

return (Class)super.h.invoke(this, m4, (Object[])null);

} catch (RuntimeException | Error var2) {

throw var2;

} catch (Throwable var3) {

throw new UndeclaredThrowableException(var3);

}

}

public final int hashCode() throws {

try {

return (Integer)super.h.invoke(this, m0, (Object[])null);

} catch (RuntimeException | Error var2) {

throw var2;

} catch (Throwable var3) {

throw new UndeclaredThrowableException(var3);

}

}

public final String value() throws {

try {

return (String)super.h.invoke(this, m3, (Object[])null);

} catch (RuntimeException | Error var2) {

throw var2;

} catch (Throwable var3) {

throw new UndeclaredThrowableException(var3);

}

}

static {

try {

m1 = Class.forName(“java.lang.Object”).getMethod(“equals”, Class.forName(“java.lang.Object”));

m2 = Class.forName(“java.lang.Object”).getMethod(“toString”);

m4 = Class.forName(“demo.annotation.MyAnnotationType”).getMethod(“annotationType”);

m0 = Class.forName(“java.lang.Object”).getMethod(“hashCode”);

m3 = Class.forName(“demo.annotation.MyAnnotationType”).getMethod(“value”);

} catch (NoSuchMethodException var2) {

throw new NoSuchMethodError(var2.getMessage());

} catch (ClassNotFoundException var3) {

throw new NoClassDefFoundError(var3.getMessage());

}

}

}

小结

走到这里,我们做一次小结:我们通过 IDEA 工具图和字节码看出我们的MyAnnotationType注解(接口)其实是继承了Annotation接口的,我们通过断点的方式和设置jdk启动参数查看到了MyAnnotationType注解的中间过程,它其实 是Java运行时生成的动态代理对象$Proxy1,该类就MyAnnotationType注解(接口)的具体实现类。

动态代理类$Proxy1是如何处理annotation.value()方法的调用?

这里我们需要去补充一下Java中动态代理的知识。这里暂时不过多介绍,下次补上。需要知道的是:动态代理方法的调用最终会传递给绑定的InvocationHandler实例的invoke方法处理。

我们继续看源码

$Proxy1

Proxy1

我们的value()方法法最终会执行(String)super.h.invoke(this, m3, (Object[])null);,而这其中的h对象类型就是InvocationHandler接口的某个实现类

Proxy

Proxy

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip1024b (备注Java)
img

最后

本人也收藏了一份Java面试核心知识点来应付面试,借着这次机会可以送给我的读者朋友们

目录:

全靠这套面试题,才让我有惊无险美团二面拿offer  (面经解析)

Java面试核心知识点

一共有30个专题,足够读者朋友们应付面试啦,也节省朋友们去到处搜刮资料自己整理的时间!

全靠这套面试题,才让我有惊无险美团二面拿offer  (面经解析)

Java面试核心知识点

已经有读者朋友靠着这一份Java面试知识点指导拿到不错的offer了

全靠这套面试题,才让我有惊无险美团二面拿offer  (面经解析)

94904)]
[外链图片转存中…(img-UYTwvoTQ-1711922194904)]
[外链图片转存中…(img-hdjLXV1k-1711922194905)]
[外链图片转存中…(img-yeDJpkgh-1711922194905)]
[外链图片转存中…(img-RNVepOMG-1711922194906)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip1024b (备注Java)
[外链图片转存中…(img-VXDWQxR7-1711922194906)]

最后

本人也收藏了一份Java面试核心知识点来应付面试,借着这次机会可以送给我的读者朋友们

目录:

[外链图片转存中…(img-5hcraCRz-1711922194906)]

Java面试核心知识点

一共有30个专题,足够读者朋友们应付面试啦,也节省朋友们去到处搜刮资料自己整理的时间!

[外链图片转存中…(img-SpxjBM26-1711922194907)]

Java面试核心知识点

已经有读者朋友靠着这一份Java面试知识点指导拿到不错的offer了

[外链图片转存中…(img-CSTjmJ00-1711922194907)]

  • 10
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值