什么是注解?
注解就是元数据,用于描述java代码的类、接口、方法、属性等等,然后应用程序可以根据注解做一些动作。比如Spring中的注解@Service,表明这是一个service类,然后Spring框架就会为其创建实例,并根据注解的参数来设置key值保存到ApplicationContext中,这些行为都是Spring框架来做的,注解只是提供元数据。详细可参见另一篇《动手写简单实现注解SpringMVC框架》。可以看到注解的作用就是给它打一个标识,表示这是一个Service类你们要处理它,另外提供一些参数,告诉你怎么处理它。注解的作用就跟xml配置文件一样。
为什么要引入注解?
使用Annotation之前(还有之后),xml被广泛应用于描述元数据。不知何时开始一些应用开发人员与架构师发现xml的维护越来越糟糕了。他们希望使用一些和代码紧耦合的东西,而不是像xml那样松耦合的代码描述。如果你在Google中搜索“xml vs. annotations”,会看到许多关于这个问题的辩论。最有趣的是xml配置其实就是为了分离代码和配置而引入的。上述两个观点可能会让你很疑惑,两者观点似乎构成一种循环,但各有利弊。下面我们通过一个例子来理解这两者的区别。
假如你想为应用设置很多常量后参数,这种情况下,xml是一个很好的选择,因为它不会同特定的代码相连。如果你想把某个方法声明为服务,那么使用annotation会更好一点,因为这种情况下需要注解和方法紧密耦合起来。
目前许多框架将xml和annotation两种方式结合使用,平衡两者之间的利弊。
注解的使用
java现在内置三个标准注解
@Override,表示当前方法定义将覆盖超类中的方法
@Deprecated 表示不赞成使用的代码
@SuppressWarnings 关闭不当编译器警告信息
java另外还提供四种注解,专门负责新注解的创建:
@Target :表示该注解可以用于什么地方,可能的ElementType参数有:
CONSTRUCTOR:构造器的声明
FIELD:域声明(包括enum实例)
LOCAL_VARIABLE:局部变量声明
METHOD:方法声明
PACKAGE:包声明
PARAMETER:参数声明
TYPE:类、接口(包括注解类型)或enum声明
@Retention:表示需要在什么级别保存该注解信息。可选的RetentionPolicy参数包括:
SOURCE:注解将被编译器丢弃
CLASS:注解在class文件中可用,但会被VM丢弃
RUNTIME:VM将在运行期间保留注解,因此可以通过反射机制读取注解的信息。
@Document:将注解包含在Javadoc中
@Inherited:允许子类继承父类中的注解
定义一个注解的方式:
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface Test { }
在注解中一般会有一些元素表示某些值,注解的元素看起来就像接口的方法,唯一的区别在于可以为其定义默认值。使用时元素不能有不确定值,即要么有默认值,要么使用时提供元素的值,且不能用null作为默认值。注解在只有一个元素且元素名为value的情况下,使用注解可以省略“value=”,直接写值即可。
下面定义一个元素
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface MyAnno{ public String description() default "no description"; }
然后使用注解
public class UseAnno{ @UseCase public void userAnnoMethod() { }
然后处理注解,注解的处理就是通过反射机制获取注解信息,然后根据注解元素的值进行特定的处理
public static void main(String[] args){ Method method = UseAnno.class.getMethod("useAnnoMethod"); MyAnno anno = method.getAnnotation(MyAnno.class); System.out.println(method.description()); //打印出“no description” }
注解的原理
java中的注解是一种继承自接口`java.lang.annotation.Annotation`的特殊接口。
下面看一下具体示例。
定义一个注解:
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface MyAnno { int count() default 1; }
经过编译之后的字节码是这样的
Classfile /D:/platform/com.cq.platform.base/com.cq.platform.base.test/target/classes/com/cq/platform/base/test/annotation/MyAnno.class Last modified 2017-12-21; size 456 bytes MD5 checksum 3731139c896044ddb4f7dfc3d7092402 Compiled from "MyAnno.java" public interface com.cq.platform.base.test.annotation.MyAnno extends java.lang.annotation.Annotation SourceFile: "MyAnno.java" RuntimeVisibleAnnotations: 0: #14(#15=[e#16.#17]) 1: #18(#15=e#19.#20) minor version: 0 major version: 52 flags: ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION Constant pool: #1 = Class #2 // com/cq/platform/base/test/annotation/MyAnno #2 = Utf8 com/cq/platform/base/test/annotation/MyAnno #3 = Class #4 // java/lang/Object #4 = Utf8 java/lang/Object #5 = Class #6 // java/lang/annotation/Annotation #6 = Utf8 java/lang/annotation/Annotation #7 = Utf8 count #8 = Utf8 ()I #9 = Utf8 AnnotationDefault #10 = Integer 1 #11 = Utf8 SourceFile #12 = Utf8 MyAnno.java #13 = Utf8 RuntimeVisibleAnnotations #14 = Utf8 Ljava/lang/annotation/Target; #15 = Utf8 value #16 = Utf8 Ljava/lang/annotation/ElementType; #17 = Utf8 TYPE #18 = Utf8 Ljava/lang/annotation/Retention; #19 = Utf8 Ljava/lang/annotation/RetentionPolicy; #20 = Utf8 RUNTIME { public abstract int count(); flags: ACC_PUBLIC, ACC_ABSTRACT AnnotationDefault: default_value: I#10}
从反编译后的信息可以看出,注解就是一个继承自“java.lang.annotation.Annotation”的接口
那么接口怎么能够设置属性呢?简单来说就是java通过动态代理的方式为你生成一个实现了“接口”‘MyAnno’的实例(对于当前的实体来说,例如类、方法、属性域等,这个代理对象是单例的),然后对该代理实例的属性赋值,这样就可以在程序运行时(如果注解设置为运行时可见的话)通过反射获取得配置信息。
具体怎么实现呢?
写一个使用该注解的类
@MyAnno(count=2) public class TestMain { public static void main(String[] args) { MyAnno anno = TestMain.class.getAnnotation(MyAnno.class); int b = anno.count(); } }
反编译代码
Classfile /D:/platform/com.cq.platform.base/com.cq.platform.base.test/target/classes/com/cq/platform/base/test/annotation/TestMain.class Last modified 2017-12-21; size 900 bytes MD5 checksum 82c13adc58b44ced5b948006f28c0f15 Compiled from "TestMain.java" public class com.cq.platform.base.test.annotation.TestMain SourceFile: "TestMain.java" RuntimeVisibleAnnotations: 0: #43(#26=I#49) minor version: 0 major version: 52 flags: ACC_PUBLIC, ACC_SUPER Constant pool: #1 = Class #2 // com/cq/platform/base/test/annotation/TestMain #2 = Utf8 com/cq/platform/base/test/annotation/TestMain #3 = Class #4 // java/lang/Object #4 = Utf8 java/lang/Object #5 = Utf8 <init> #6 = Utf8 ()V #7 = Utf8 Code #8 = Methodref #3.#9 // java/lang/Object."<init>":()V #9 = NameAndType #5:#6 // "<init>":()V #10 = Utf8 LineNumberTable #11 = Utf8 LocalVariableTable #12 = Utf8 this #13 = Utf8 Lcom/cq/platform/base/test/annotation/TestMain; #14 = Utf8 main #15 = Utf8 ([Ljava/lang/String;)V #16 = Class #17 // com/cq/platform/base/test/annotation/MyAnno #17 = Utf8 com/cq/platform/base/test/annotation/MyAnno #18 = Methodref #19.#21 // java/lang/Class.getAnnotation:(Ljava/lang/Class;)Ljava/lang/annotation/Annotation; #19 = Class #20 // java/lang/Class #20 = Utf8 java/lang/Class #21 = NameAndType #22:#23 // getAnnotation:(Ljava/lang/Class;)Ljava/lang/annotation/Annotation; #22 = Utf8 getAnnotation #23 = Utf8 (Ljava/lang/Class;)Ljava/lang/annotation/Annotation; #24 = InterfaceMethodref #16.#25 // com/cq/platform/base/test/annotation/MyAnno.count:()I #25 = NameAndType #26:#27 // count:()I #26 = Utf8 count #27 = Utf8 ()I #28 = Fieldref #29.#31 // java/lang/System.out:Ljava/io/PrintStream; #29 = Class #30 // java/lang/System #30 = Utf8 java/lang/System #31 = NameAndType #32:#33 // out:Ljava/io/PrintStream; #32 = Utf8 out #33 = Utf8 Ljava/io/PrintStream; #34 = Methodref #35.#37 // java/io/PrintStream.println:(I)V #35 = Class #36 // java/io/PrintStream #36 = Utf8 java/io/PrintStream #37 = NameAndType #38:#39 // println:(I)V #38 = Utf8 println #39 = Utf8 (I)V #40 = Utf8 args #41 = Utf8 [Ljava/lang/String; #42 = Utf8 anno #43 = Utf8 Lcom/cq/platform/base/test/annotation/MyAnno; #44 = Utf8 b #45 = Utf8 I #46 = Utf8 SourceFile #47 = Utf8 TestMain.java #48 = Utf8 RuntimeVisibleAnnotations #49 = Integer 2 { public com.cq.platform.base.test.annotation.TestMain(); flags: ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: aload_0 1: invokespecial #8 // Method java/lang/Object."<init>":()V 4: return LineNumberTable: line 3: 0 LocalVariableTable: Start Length Slot Name Signature 0 5 0 this Lcom/cq/platform/base/test/annotation/TestMain; public static void main(java.lang.String[]); flags: ACC_PUBLIC, ACC_STATIC Code: stack=2, locals=3, args_size=1 0: ldc #1 // class com/cq/platform/base/test/annotation/TestMain 2: ldc #16 // class com/cq/platform/base/test/annotation/MyAnno 4: invokevirtual #18 // Method java/lang/Class.getAnnotation:(Ljava/lang/Class;)Ljava/lang/annotation/Annotation; 7: checkcast #16 // class com/cq/platform/base/test/annotation/MyAnno 10: astore_1 11: aload_1 12: invokeinterface #24, 1 // InterfaceMethod com/cq/platform/base/test/annotation/MyAnno.count:()I 17: istore_2 18: getstatic #28 // Field java/lang/System.out:Ljava/io/PrintStream; 21: iload_2 22: invokevirtual #34 // Method java/io/PrintStream.println:(I)V 25: return LineNumberTable: line 5: 0 line 6: 11 line 7: 18 line 8: 25 LocalVariableTable: Start Length Slot Name Signature 0 26 0 args [Ljava/lang/String; 11 15 1 anno Lcom/cq/platform/base/test/annotation/MyAnno; 18 8 2 b I }
RuntimeVisibleAnnotations: 0:#43(#26=I#49)
可以看到注解“MyAnno”的属性设置是在编译时就确定了的。
然后可以在项目的com/sun/proxy包下看到代理类““$Proxy1.class”。其中‘com.sun.proxy’是jdk动态代理生成对象时的默认包名。可以看到这个代理类继承自“java.lang.reflect.Proxy”类,又实现了“接口”MyAnno。
文章来自:http://www.importnew.com/10294.html
https://www.cnblogs.com/huajiezh/p/5263849.html
https://www.zhihu.com/question/24401191中曹旭东的回答
相关推荐
以上就是Java 5.0中关于注释以及其他相关特性的详细解释,这些改进不仅提高了代码的可读性和维护性,也增强了Java作为企业级开发语言的能力。对于开发者来说,理解并熟练掌握这些特性是非常重要的,因为它们已经成为...
最后,本书可能还会涉及Java的最新特性,比如注解、泛型、枚举、动态代理等,这些都是Java语言不断演进的体现,有助于开发者保持与时俱进。 通过阅读《Java2编程详解》,无论是初学者还是有经验的开发者,都能从中...
在编程世界中,Java注解(Annotation)是一个强大的工具,它允许程序员在代码中嵌入元数据,这些元数据不直接影响程序的运行,但可以被编译器或运行时环境用来进行各种处理。Android开发中,注解同样扮演着重要角色...
本篇文章将深入探讨Java注解的使用及其在实际开发中的应用。 首先,我们了解Java注解的基本结构。一个注解以`@`符号开头,后面跟着注解类型,如`@Override`或`@Deprecated`。注解可以带有参数,参数以键值对的形式...
10. **Java高级特性**:包括反射、动态代理、注解、枚举、集合工厂方法、泛型等,这些都是Java2平台的进阶特性,它们极大地增强了Java的灵活性和可维护性。 通过阅读《Java2编程详解》,读者可以系统地学习和掌握...
Java扫雷代码详解 本篇文章主要讲解了Java扫雷程序的代码实现,通过对扫雷程序的代码注释,帮助初学者更好地理解扫雷程序的工作原理。 Java扫雷程序的设计 Java扫雷程序的设计主要分为三个步骤:第一步,extends ...
Java注解技术是Java平台中一个非常重要的特性,它为元数据提供了强大的支持,使得开发者能够在代码中嵌入额外的信息,这些信息可以被编译器、JVM或第三方工具读取和处理。在本视频教程中,我们将深入探讨Java注解的...
9. **反射与注解**:探讨Java运行时动态类型的能力,以及如何使用注解来添加元数据。 10. **Java的高级特性**:可能包含泛型、枚举、匿名内部类、Lambda表达式等Java 5及以后版本引入的新特性。 11. **Java EE基础...
Java 元注解详解 Java 元注解是 Java 注解的重要特性,它们是用于修饰注解的注解。Java 元注解的作用是通过使用 Java 元注解,可以为注解指定:作用范围、生命周期、是否可重复和是否继承等信息,从而提高注解的...
Java 注解详解 Java 注解(Annotation)是 JDK 5.0 及以后版本引入的一个特性。它是一个新的类型,与接口类似,位于同一个层次,称为 Java 的一个类型(TYPE)。注解可以声明在包、类、字段、方法、局部变量、方法...
java 注释规范详解 java 注释规范是 Java 开发过程中不可或缺的一部分,它的目的是让项目中所有的文档都看起来像一个人写的,增加可读性,减少项目组中因为换人而带来的损失。java 注释规范可以分为三种:单行注释...
综上所述,《JAVA WEB开发详解》这本书系统地介绍了JAVA Web开发中涉及到的关键技术和实践方法,从XML的基础到Servlet和JSP的深入解析,再到实际应用中的最佳实践,为开发者提供了全面的学习指南。
Hibernate允许开发者使用Java对象直接操作数据库,通过配置文件或注解定义对象-表的映射。MyBatis则提供了SQL映射文件,将SQL语句与Java代码分离,提高了可维护性。 在实际项目中,我们还需要关注数据库连接池的...
### Java注解详解 #### 一、注解(Annotation)简介 注解是Java语言自JDK 5.0版本引入的一种新特性,用于在代码中添加元数据(即关于数据的数据)。它允许开发人员向Java编译器、工具和其他框架提供额外的信息,而不...
Java 反射机制是 Java 语言中的一个重要特性,它允许程序在运行时动态地获取类的信息(如类名、属性、方法等)并调用对象的方法,甚至修改对象的状态。这一机制极大地增强了 Java 程序的灵活性和可扩展性,尤其是在...
### Java注释详解 #### 注释的原则 在Java编程中,注释是非常重要的组成部分,它不仅帮助开发者理解代码的功能和用途,还提高了代码的可维护性和可读性。以下是关于Java注释的一些基本原则: 1. **注释形式的统一...
### Eclipse Java注释模板设置详解 在开发过程中,良好的代码注释习惯对于提高代码的可读性和维护性具有重要作用。Eclipse作为一款流行的Java集成开发环境(IDE),提供了丰富的功能来帮助开发者更好地管理代码和...
Java注解特性详解[汇编].pdf
### JAVA编程注释规范详解 #### 一、引言 注释是软件开发过程中不可或缺的部分,它能够帮助开发者更好地理解代码的功能与实现细节,提高代码的可读性和可维护性。在Java编程语言中,良好的注释习惯尤为重要,因为...
Java 程序使用 JPA 注解详解 Java 持久层 API(Java Persistence API)是一种 Java 应用程序接口,用于访问、管理和持久化数据之间的关系。JPA 使用注解来定义实体类与数据库表之间的映射关系,本文将详细介绍 JPA ...