码农知识堂 - 1000bd
  •   Python
  •   PHP
  •   JS/TS
  •   JAVA
  •   C/C++
  •   C#
  •   GO
  •   Kotlin
  •   Swift
  • 04. JAVA注解机制


    0.Java 注解的定义

    0.注解的使用场景

    ● 提供信息给编译器:利用注解来来探测错误和警告信息

    ● 编译阶段时的处理:软件工具利用注解来生成代码、Html文档或其他相应处理;

    ● 运行时的处理:某些注解可以在程序运行的时候接受代码的提取;

    1.注解的作用或者意义

    注解本身没有任何意义,单独的注解就是一种注释,他需要结合其他如反射、插桩等技术才有意义;

    Java注解(Annotation) 又称Java标注,是JDK5.0引入的一种的一种注释机制,用于为Java代码提供元数据,

    即有关于程序但不属于程序本身的数据(用来描述数据的数据),注解对它们注解的代码的操作没有直接影响;

    ┌---Deprecated

    ElementType---------┐ ├---Documented

    1..n | ├---Inherited

    <<接口>> <-----Override

    Annotation ├---Retention

    1 | ├---Target 目标注解

    RetentionPolicy-----┘ └---...

    2.注解的声明

    java中所有的注解,默认实现Annotation接口:

    1. package java.lang.annotation;
    2. public interface Annotation{
    3. boolean equals(Object obj);
    4. int hashCode();
    5. String toString();
    6. Classextends Annotation> annotationType();
    7. }
    8. //与声明一个"Class"不同的是,注解的声明使用 @interface 关键字
    9. public @interface AnnotationTest {
    10. }

    3.元注解(注解上面的注解)

    在定义注解时, 注解类也能够使用其他的注解声明, 对注解类型进行注解的注解类,我们称之为 meta annotation(元注解)

    /ˈmetə/ /ˌænəˈteɪʃn/, 一般的我们在定义自定义注解时, 需要指定的元注解有两个:@Target @Retention, 另外还有

    @Documented、@Inherited 和 @Repeatable 5种元注解,@Documented用于被javadoc工具提取成文档, @Inherited表示

    继承的意思,允许子类继承父类中定义的注解, @Repeatable 可重复应用的意思,注解的值可同时取多个, 在java1.8引入;

    1> @Target(作用域)

    限制注解作用域的注解(目标作用域)

    注解标记另一个注解,以限制可以应用注解的java元素类型,目标注解指定一下元素之一作为其值:

    ● ElementType.ANNOTATION_TYPE 可应用于注解类型(annotation_type);

    ● ElementType.CONSTRUCTOR 可用于构造方法(constructor);

    ● ElementType.FIELD 可用于字段或属性(field);

    ● ElementType.LOCAL_VARIABLE 可用于局部变量(variable);

    ● ElementType.METHOD 可用于方法级注解(method);

    ● ElementType.PACKAGE 可用于包声明 (package);

    ● ElementType.PARAMETER 可用于方法中的参数 (parameter);

    ● ElementType.TYPE 可用于类的 任何元素

    示例:

    1. //@Target(ElementType.TYPE)//只能在类上面标记该注解
    2. Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD})//允许在类与类属性与方法上标记该注解
    3. @Retention(RetentionPolicy.SOURCE)//注解保留在源码中
    4. public @interface Test {
    5. String value(); //无默认值, 注意在使用注解时,如果定义的注解中的类型元素无默认值,则必须进行传值
    6. int age() default 1;//有默认值
    7. }

    注解类型元素

    在上文元注解中,允许在使用注解时传递参数,我们也能自定义注解的主体 包含 annotation type element

    (注解类型元素)声明, 它们看起来很像方法,可以定义可选的默认值; @AnnotationTest(value="帅",age=2)

    @Test("帅") String value();//如果成员变量只存在value元素需要传值的情况,否则可省略:@注解名(元素名=值)

    @如果不光有value 还有其他元素 如: int id(); String value(); @Test(id=1 value="") 都要写成key-value

    1. @Test("") //对应 @Target(ElementType.TYPE)
    2. public class AnnotationTest {
    3. @Test("")//对应 @Target(ElementType.FIELD)
    4. int i;
    5. @Test("")//对应 @Target(ElementType.METHOD)
    6. public static void main(String[] args) {
    7. System.out.println("Hello Java");
    8. }
    9. }

    2> @Retention(存储方式)

    限制注解存储方式的注解

    注解指定标记注解的存储方式

    ● RetentionPolicy.SOURCE -标记的注解仅保留在源级别中,并被编译器忽略; 编译成class就会被擦除掉 只在源码中

    ● RetentionPolicy.CLASS -标记的注解在编译时有编译器保留,但Java虚拟机(JVM)会忽略; 保留在class 字节码中

    ● RetentionPolicy.RUNTIME-标记的注解由JVM保留,因此运行时环境可以使用它; 结合反射技术

    @Retention(保留) 三个值中 Source

    根据@Retention 元注解定义的注解存储方式,注解可以被在三种场景下使用:

    1) Source

    Retention.SOURCE,作用于源码级别的注解,可以提供IDE语法检查,APT等场景使用;

    在类中使用SOURCE级别的注解,会在其编译后的class中被擦除;

    APT注解处理器:

    其用于处理注解,编写好Java源文件,需要经过javac的编译,翻译为虚拟机能够加载解析的字节Class文件,注解处理器是

    javac自带的一个工具,用来在编译时扫描处理注解信息,你可以为某些注解注册字节的注解处理器,注册的注解处理器由

    javac调起,并将注解信息传递给注解处理器进行处理;

    注解处理器是对注解应用最广泛的场景,在Glide、EventBus3、ButterKnife、Tinker、ARouter等常用框架中都有

    注解处理器的身影,,这些框架中对注解的定义并不是SOURCE级别,更多的是CLASS级别;

    1. com.xzh.compiler.TestProcessor
    2. new Module->compiler/java/com.xzh.compiler/MyProcessor ↓
    3. resources/META-INF/services/javax.annotation.processing.Processor
    4. app/build.gradle/ annotationProcessor project(':compiler')
    5. //采集到所有的注解信息->Element->注解处理程序 .java->javac->.class
    6. //APT 注解处理器 Annotation Processor Tools
    7. @SupportedAnnotationTypes("com.xzh.study.annotation.WeekDay")
    8. public class TestProcessor extends AbstractProcessor {
    9. @Override //获取需要处理的注解对象(编译的程序中使用的注解)
    10. public boolean process(Set annotations, RoundEnvironment roundEnv) {
    11. for (TypeElement typeElement : annotations) {
    12. Messager messager = processingEnv.getMessager();
    13. messager.printMessage(Diagnostic.Kind.NOTE,"================>");
    14. }
    15. return false;
    16. }
    17. }
    18. //IDE语法检查 在Android开发中,support-annotations与androidx.annotation中均有提供@IntDef注解,此注解的定义如下
    19. @Retention(SOURCE) //源码级别注解
    20. @Target({ANNOTATION_TYPE})
    21. public @interface IntDef{
    22. int[] value() default {};
    23. boolean flag() default false;
    24. boolean open() default false;
    25. }

    Java中Enum(枚举)的实质是单例的静态成员变量,在运行期所有枚举类作为单例,全部加载到内存中,比常量多5到10倍的内存占用,一个对象占用: 12对象头+成员所占内存+8字节对齐;此注解的意义是能够取代枚举,实现如方法入参限制; 枚举是对象;如:我们定义方法setCurrentDay, 此方法接收参数Day 需要在:SUNDAY和MONDAY中选一个,如果使用枚举能够实现为

    1. public enum WeekDay{
    2. SUNDAY, MONDAY,TUESDAY,Wednesday,ThursDay,Firday,Saturday
    3. }
    4. public class AnnotationTest {
    5. static final int SUNDAY = 0;
    6. static final int MONDAY = 1;
    7. static final int TUESDAY = 2;
    8. static final int Wednesday = 3;
    9. static final int ThursDay = 4;
    10. static final int Firday = 5;
    11. static final int Saturday = 6;
    12. @WeekDay //定义的时候初始化时可检测
    13. private static int currentIntDay;//在这里检测 比如只能赋值0-6;
    14. //语法检查 调用setCurrentDay方法由于参数是采用基本数据类型int,
    15. //将无法进行类型限定,此时使用@IntDef增加自定义注解:
    16. @IntDef({SUNDAY, MONDAY,TUESDAY,Wednesday,ThursDay,Firday,Saturday})
    17. @Target({ElementType.PARAMETER, ElementType.FIELD})
    18. @Retention(RetentionPolicy.SOURCE)
    19. @interface WeekDay {}
    20. public static void setCurrentDay(@WeekDay int currentDay) {
    21. currentIntDay = currentDay;
    22. }
    23. public static void main(String[] args) {
    24. System.out.println("Hello Java");
    25. setCurrentDay(SUNDAY);
    26. }
    27. }

    2) CLASS

    定义为Class的注解,会保留在class文件中,但会被虚拟机忽略(即无法在运行期反射获取注解),如字节码操作,AspectJ

    热修复Robust中应用此场景,所谓的字节码操作即为,直接修改字节码Class文件以达到修改代码执行逻辑的目的;

    示例:是否登录--[已登录(通过验证)|未登录(进入登录)]

    如果使用不同的编程方式,需要在代码中进行if-else的判断,若存在10个判断点,需要在每个判断点加入此项判断;此时

    我们可以借助AOP(面向切面)编程思想,将程序中所有功能点划分为: 需要登录 和 无需登录两种类型,即两个切面,

    对于切面的区分即可采用注解

    注解的提取

    注解与反射,注解通过该反射获取,首先可以通过Class对象的isAnnotationPresent(Class cls)

    判断是否用了某注解,然后通过 getAnnotation(Class aCls) 获取注解的对象,获取的注解不为null,就可调用属性了;

    示例:实现intent传值自动写入对应成员中

    1. //注解类
    2. @Target(ElementType.FIELD)
    3. @Retention(RetentionPolicy.RUNTIME)
    4. public @interface AutoWrite {
    5. String value() default "";
    6. }
    7. //该工具主要实现intent传值自动写入对应成员中
    8. public class InjectUtils {
    9. public static void injectExtra(Activity activity) {
    10. //获取总数据
    11. Intent intent = activity.getIntent();
    12. Bundle extras = intent.getExtras();
    13. if (extras == null) return;
    14. //获取成员
    15. Classextends Activity> cls = activity.getClass();
    16. Field[] fields = cls.getDeclaredFields();
    17. for (Field field : fields) {
    18. if (field.isAnnotationPresent(AutoWrite.class)) {
    19. AutoWrite autoWrite = field.getAnnotation(AutoWrite.class);
    20. if (autoWrite == null) return;
    21. //获得注解中的key
    22. String key = autoWrite.value();
    23. if (extras.containsKey(key)) {
    24. Object value = extras.get(key);
    25. //Parcelable数组类型不能直接设置,其他都可以
    26. //获得单个元素类型
    27. Class type = field.getType().getComponentType();
    28. //获得数组单个元素类型
    29. if (field.getType().isArray() && Parcelable.class.isAssignableFrom(type)) {
    30. Object[] array = (Object[]) value;
    31. if (array != null)
    32. value = Arrays.copyOf(array, array.length, (Classextends Object[]>) field.getType());
    33. }
    34. field.setAccessible(true);//开放访问权限
    35. try {
    36. //反射设置属性的值 传实例对象
    37. field.set(activity, value);
    38. } catch (IllegalAccessException e) {
    39. e.printStackTrace();
    40. }
    41. }
    42. }
    43. }
    44. }
    45. }
    46. //进行传值操作
    47. public class MainActivity extends AppCompatActivity {
    48. public void test(View view) {
    49. Intent intent = new Intent(this, TestActivity.class);
    50. intent.putExtra("name", "NorthStar");
    51. intent.putExtra("age", 18);
    52. intent.putExtra("isMen", true);
    53. startActivity(intent);
    54. }
    55. }
    56. //使用注解赋值
    57. public class TestActivity extends AppCompatActivity {
    58. @AutoWrite(value = "name")
    59. String name;
    60. @AutoWrite(value = "age")
    61. int age;
    62. @AutoWrite(value = "isMen")
    63. boolean isMen;
    64. public static final String TAG = "reflex";
    65. @Override
    66. protected void onCreate(Bundle savedInstanceState) {
    67. super.onCreate(savedInstanceState);
    68. setContentView(R.layout.activity_test);
    69. InjectUtils.injectExtra(this);
    70. initData();
    71. }
    72. public void initData() {
    73. Log.d(TAG, "name==>" + name + " ,age==>" + age + " ,isMen==>" + isMen);
    74. }
    75. }
  • 相关阅读:
    Django--inclusion_tag
    初识MySQL数据库
    37.解数独(内含回溯算法重要思路)
    wireshark数据结构
    【ManageEngine】网络性能监控工具
    < 文件资源管理器 > 和 < 此电脑 > 有什么区别?
    【ESP32 + Edge Impulse平台】模拟多传感器数据融合实验测试
    Redis环境配置
    简单DIV+CSS学生网页设计——电影请以你的名字呼唤我(4页)带音乐特效
    数据结构——双向链表(双向连接的图解、双向链表的创建、操作双向链表)
  • 原文地址:https://blog.csdn.net/x910131/article/details/126022925
  • 最新文章
  • 攻防演习之三天拿下官网站群
    数据安全治理学习——前期安全规划和安全管理体系建设
    企业安全 | 企业内一次钓鱼演练准备过程
    内网渗透测试 | Kerberos协议及其部分攻击手法
    0day的产生 | 不懂代码的"代码审计"
    安装scrcpy-client模块av模块异常,环境问题解决方案
    leetcode hot100【LeetCode 279. 完全平方数】java实现
    OpenWrt下安装Mosquitto
    AnatoMask论文汇总
    【AI日记】24.11.01 LangChain、openai api和github copilot
  • 热门文章
  • 十款代码表白小特效 一个比一个浪漫 赶紧收藏起来吧!!!
    奉劝各位学弟学妹们,该打造你的技术影响力了!
    五年了,我在 CSDN 的两个一百万。
    Java俄罗斯方块,老程序员花了一个周末,连接中学年代!
    面试官都震惊,你这网络基础可以啊!
    你真的会用百度吗?我不信 — 那些不为人知的搜索引擎语法
    心情不好的时候,用 Python 画棵樱花树送给自己吧
    通宵一晚做出来的一款类似CS的第一人称射击游戏Demo!原来做游戏也不是很难,连憨憨学妹都学会了!
    13 万字 C 语言从入门到精通保姆级教程2021 年版
    10行代码集2000张美女图,Python爬虫120例,再上征途
Copyright © 2022 侵权请联系2656653265@qq.com    京ICP备2022015340号-1
正则表达式工具 cron表达式工具 密码生成工具

京公网安备 11010502049817号