「java元注解解析」java常用元注解
本篇文章给大家谈谈java元注解解析,以及java常用元注解对应的知识点,希望对各位有所帮助,不要忘了收藏本站喔。
本文目录一览:
- 1、JAVA 对元注解的理解
- 2、java元注解,局部注解怎么解析
- 3、深入浅出Java注解 / Java注解是如何工作的
- 4、深入理解Spring注解机制:合并注解的合成
- 5、java内部注解是如何实现的
- 6、java注解是怎么实现的
JAVA 对元注解的理解
额,我的理解是,元注解有4个,是对普通注解的一种限制,@Target是对注解使用地方的先知,属性里边有那些,可以类注解,属性注解,方法注解什么的,当然还有其他三个,不过忘了
java元注解,局部注解怎么解析
对于局部变量的注解只能在源码级别上进行处理,类文件并不描述局部变量。因此,所有的局部变量注解在编译完一个类的时候就会被遗弃掉。同样地,对包的注解不能在源码级别之外存在。
在文件package-info.java中注解一个包,该文件只包含以注解先导的包声明
包注解需要在特定文件:package-info.java,中添加,获取如下:
Package pkg = TestAnnotation.class.getPackage();
boolean hasAnnotation=pkg.isAnnotationPresent(Xpc.class);
if (hasAnnotation){
Xpc xpc=pkg.getAnnotation(Xpc.class);
System.out.println("package:" + pkg.getName());
System.out.println("id=" + xpc.id());
System.out.println("name=" + xpc.name());
System.out.println("gid=" + xpc.gid());
}
深入浅出Java注解 / Java注解是如何工作的
给java注解下个定义
用一个词就可以描述注解,那就是元数据,即一种描述数据的数据。所以,可以说注解就是源代码的元数据。Annotation是一种应用于类、方法、参数、变量、构造器及包声明中的特殊修饰符。它是一种由JSR-175标准选择用来描述元数据的一种工具。
注解的本质
Annotations仅仅是元数据,和业务逻辑无关。理解起来有点困难,但就是这样。如果Annotations不包含业务逻辑,那么必须有人来实现这些逻辑。元数据的"使用者"来做这个事情。Annotations仅仅提供它定义的属性(类/方法/包/域)的信息。Annotations的"使用者"(同样是一些代码)来读取这些信息并实现必要的逻辑。当我们使用Java的标注Annotations(例如@Override)时,JVM就是一个“使用者”,它在字节码层面工作。mok:"当我们使用用于Servlet的注解(例如@WebInitParam)时,容器就是一个'使用者';当我们使用框架提供的注解时,框架中的某个组件会负责做读取后的逻辑处理。"
深入理解Spring注解机制:合并注解的合成
众所周知, spring 从 2.5 版本以后开始支持使用注解代替繁琐的 xml 配置,到了 springboot 更是全面拥抱了注解式配置。平时在使用的时候,点开一些常见的等注解,会发现往往在一个注解上总会出现一些其他的注解,比如 @Service :
大部分情况下,我们可以将 @Service 注解等同于 @Component 注解使用,则是因为 spring 基于其 JDK 对 元注解的机制 进行了扩展。
在 java 中,元注解是指可以注解在其他注解上的注解,spring 中通过对这个机制进行了扩展,实现了一些原生 JDK 不支持的功能,比如允许在注解中让两个属性互为别名,或者将一个带有元注解的子注解直接作为元注解看待,或者在这个基础上,通过 @AliasFor 或者同名策略让子注解的值覆盖元注解的值。
本文将基于 spring 源码 5.2.x 分支,解析 spring 如何实现这套功能的。
这是系列的第三篇文章,将详细介绍 Spring 是如何在经过搜索与属性映射后,将处理后的注解合成为合并注解的。
相关文章:
我们在前文了解用于搜索注解的合并注解聚合 MergedAnnotations 与用于完成注解属性映射的 AnnotationTypeMappings 和 AnnotationTypeMapping ,现在我们需要知道在 MergedAnnotations 这个容器中, AnnotationTypeMappings 和 AnnotationTypeMapping 是如何转为一个我们所需要的合并注解 MergedAnnotation 的。
与前文一样,我们以 AnnotatedElementUtils.findMergedAnnotations 方法作为入口:
我们在上文顺着 MergedAnnotations.get 一路找到 TypeMappedAnnotations.MergedAnnotationFinder 的 process 方法,在这里我们目睹了一个普通的注解的元注解被解析为 AnnotationTypeMappings 与 AnnotationTypeMapping 的过程:
该方法是 AnnotationTypeMapping 转为 MergedAnnotation 的关键。
TypeMappedAnnotation 是 MergedAnnotation 一个通用实现,在大部分情况下,我们所说的合并注解其实指的就是这个类。
通过它的构造方法我们得以了解其创建过程:
可以看得出, TypeMappedAnnotation 基本可以认为是 AnnotationTypeMapping 的包装类,它以一个 AnnotationTypeMapping 实例作为数据源,从而提供一些关于映射后的属性的相关功能。
回到 AnnotatedElementUtils.findMergedAnnotations ,我们可以看到,在通过 MergedAnnotations 获得了一个 MergedAnnotation 对象——实际上是 TypeMappedAnnotation 对象——之后,又调用了 MergedAnnotation.synthesize 方法,将 MergedAnnotation 转成了一个调用方指定类型的注解对象。
该方法先调用了 AbstractMergedAnnotation 的 synthesize 方法:
随后再调用了实现类 TypeMappedAnnotation 的 synthesize 方法:
继续点开 createSynthesized :
而 SynthesizedMergedAnnotationInvocationHandler 是一个用于 JDK 动态代理的 InvocationHandler ,我们不需要完全站看,仅需看看它的构造函数与 InvocationHandler.invoke 就能明白它的运作机制了:
至此,合并注解的合成机制已经很明确了:
承接上文,当我们使用 MergedAnnotation.synthesize 方法时,我们可能会得到两种对象:
而通过注解代理对象取值时,这些方法会被代理到 SynthesizedMergedAnnotationInvocationHandler 中存放的 MergedAnnotation 对象上,从而让这个代理对象通过原始注解的属性,获得与原始注解不一样的属性值。
当我们调用代理对象的属性值时,它会在 SynthesizedMergedAnnotationInvocationHandler 中,通过 invoke 代理到对应的方法上:
这里我们代理对象是如何获取注解属性值的:
这里的 MergedAnnotation.getValue 最终在经过多次跳转后,调到 TypeMappedAnnotation.getAttributeValue 上:
而这边的 getValue 方法就是真正要获取属性值的地方。
这一步有点复杂,主要是根据不同的情况,通过 AnnotationTypeMapping 中的几个属性映射数组,包括 aliasMappings 、 conventionMappings , annotationValueMappings 与 annotationValueSource 来确定最终用于取值的 AnnotationTypeMapping 对象与调用的方法在 AttributeMethods 中的下标:
至此,获取属性值的方法流程也走完了。
在这一章,我们了解了当通过 MergedAnnotations 获得注解并解析得到 AnnotationTypeMapping 后, AnnotationTypeMapping 是如何再转为我们所需的 MergedAnnotation ,以及在此之后, MergedAnnotation 又是如何生成我们最终所需要的代理注解的。
简而言之,当解析注解的元注解获得所需的 AnnotationTypeMapping 后, MergedAnnotation 会判断 AnnotationTypeMapping 是否发生过属性映射,如果没有则返回该映射对象对应的原始注解,否则就通过 SynthesizedMergedAnnotationInvocationHandler 生成一个对应类型的 JDK 动态代理对象。
当我们通过代理对象去调用注解的方法,获取注解的属性的时候, SynthesizedMergedAnnotationInvocationHandler 会把方法代理到对应的内部方法中,而获取属性时,还会通过 MergedAnnotation.getValue ,最终绕到 AnnotationTypeMapping 中获取被映射后的属性值。
java内部注解是如何实现的
用一个词就可以描述注解,那就是元数据,即一种描述数据的数据。所以,可以说注解就是源代码的元数据。比如,下面这段代码:
@Override
public String toString() {
return "This is String Representation of current object.";
}
上面的代码中,我重写了toString()方法并使用了@Override注解。但是,即使我不使用@Override注解标记代码,程序也能够正常执行。那么,该注解表示什么?这么写有什么好处吗?事实上,@Override告诉编译器这个方法是一个重写方法(描述方法的元数据),如果父类中不存在该方法,编译器便会报错,提示该方法没有重写父类中的方法。如果我不小心拼写错误,例如将toString()写成了toStrring(){double r},而且我也没有使用@Override注解,那程序依然能编译运行。但运行结果会和我期望的大不相同。现在我们了解了什么是注解,并且使用注解有助于阅读程序。
Annotation是一种应用于类、方法、参数、变量、构造器及包声明中的特殊修饰符。它是一种由JSR-175标准选择用来描述元数据的一种工具。
为什么要引入注解?
使用Annotation之前(甚至在使用之后),XML被广泛的应用于描述元数据。不知何时开始一些应用开发人员和架构师发现XML的维护越来越糟糕了。他们希望使用一些和代码紧耦合的东西,而不是像XML那样和代码是松耦合的(在某些情况下甚至是完全分离的)代码描述。如果你在Google中搜索“XML vs. annotations”,会看到许多关于这个问题的辩论。最有趣的是XML配置其实就是为了分离代码和配置而引入的。上述两种观点可能会让你很疑惑,两者观点似乎构成了一种循环,但各有利弊。下面我们通过一个例子来理解这两者的区别。
假如你想为应用设置很多的常量或参数,这种情况下,XML是一个很好的选择,因为它不会同特定的代码相连。如果你想把某个方法声明为服务,那么使用Annotation会更好一些,因为这种情况下需要注解和方法紧密耦合起来,开发人员也必须认识到这点。
另一个很重要的因素是Annotation定义了一种标准的描述元数据的方式。在这之前,开发人员通常使用他们自己的方式定义元数据。例如,使用标记interfaces,注释,transient关键字等等。每个程序员按照自己的方式定义元数据,而不像Annotation这种标准的方式。
目前,许多框架将XML和Annotation两种方式结合使用,平衡两者之间的利弊。
Annotation是如何工作的?怎么编写自定义的Annotation?
在讲述这部分之前,建议你首先下载Annotation的示例代码AnnotationsSample.zip 。下载之后放在你习惯使用的IDE中,这些代码会帮助你更好的理解Annotation机制。
编写Annotation非常简单,可以将Annotation的定义同接口的定义进行比较。我们来看两个例子:一个是标准的注解@Override,另一个是用户自定义注解@Todo。
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
对于@Override注释你可能有些疑问,它什么都没做,那它是如何检查在父类中有一个同名的函数呢。当然,不要惊讶,我是逗你玩的。@Override注解的定义不仅仅只有这么一点代码。这部分内容很重要,我不得不再次重复:Annotations仅仅是元数据,和业务逻辑无关。理解起来有点困难,但就是这样。如果Annotations不包含业务逻辑,那么必须有人来实现这些逻辑。元数据的用户来做这个事情。Annotations仅仅提供它定义的属性(类/方法/包/域)的信息。Annotations的用户(同样是一些代码)来读取这些信息并实现必要的逻辑。
当我们使用Java的标注Annotations(例如@Override)时,JVM就是一个用户,它在字节码层面工作。到这里,应用开发人员还不能控制也不能使用自定义的注解。因此,我们讲解一下如何编写自定义的Annotations。
我们来逐个讲述编写自定义Annotations的要点。上面的例子中,你看到一些注解应用在注解上。
J2SE5.0版本在 java.lang.annotation提供了四种元注解,专门注解其他的注解:
@Documented –注解是否将包含在JavaDoc中
@Retention –什么时候使用该注解
@Target? –注解用于什么地方
@Inherited – 是否允许子类继承该注解
@Documented–一个简单的Annotations标记注解,表示是否将注解信息添加在java文档中。
@Retention– 定义该注解的生命周期。
RetentionPolicy.SOURCE – 在编译阶段丢弃。这些注解在编译结束之后就不再有任何意义,所以它们不会写入字节码。@Override, @SuppressWarnings都属于这类注解。
RetentionPolicy.CLASS – 在类加载的时候丢弃。在字节码文件的处理中有用。注解默认使用这种方式。
RetentionPolicy.RUNTIME– 始终不会丢弃,运行期也保留该注解,因此可以使用反射机制读取该注解的信息。我们自定义的注解通常使用这种方式。
@Target – 表示该注解用于什么地方。如果不明确指出,该注解可以放在任何地方。以下是一些可用的参数。需要说明的是:属性的注解是兼容的,如果你想给7个属性都添加注解,仅仅排除一个属性,那么你需要在定义target包含所有的属性。
ElementType.TYPE:用于描述类、接口或enum声明
ElementType.FIELD:用于描述实例变量
ElementType.METHOD
ElementType.PARAMETER
ElementType.CONSTRUCTOR
ElementType.LOCAL_VARIABLE
ElementType.ANNOTATION_TYPE 另一个注释
ElementType.PACKAGE 用于记录java文件的package信息
@Inherited – 定义该注释和子类的关系
那么,注解的内部到底是如何定义的呢?Annotations只支持基本类型、String及枚举类型。注释中所有的属性被定义成方法,并允许提供默认值。
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface Todo {
public enum Priority {LOW, MEDIUM, HIGH}
public enum Status {STARTED, NOT_STARTED}
String author() default "Yash";
Priority priority() default Priority.LOW;
Status status() default Status.NOT_STARTED;
}
下面的例子演示了如何使用上面的注解。
@Todo(priority = Todo.Priority.MEDIUM, author = "Yashwant", status = Todo.Status.STARTED)
public void incompleteMethod1() {
//Some business logic is written
//But it’s not complete yet
}
如果注解中只有一个属性,可以直接命名为“value”,使用时无需再标明属性名。
@interface Author{
String value();
}
@Author("Yashwant")
public void someMethod() {
}
但目前为止一切看起来都还不错。我们定义了自己的注解并将其应用在业务逻辑的方法上。现在我们需要写一个用户程序调用我们的注解。这里我们需要使用反射机制。如果你熟悉反射代码,就会知道反射可以提供类名、方法和实例变量对象。所有这些对象都有getAnnotation()这个方法用来返回注解信息。我们需要把这个对象转换为我们自定义的注释(使用 instanceOf()检查之后),同时也可以调用自定义注释里面的方法。看看以下的实例代码,使用了上面的注解:
Class businessLogicClass = BusinessLogic.class;
for(Method method : businessLogicClass.getMethods()) {
Todo todoAnnotation = (Todo)method.getAnnotation(Todo.class);
if(todoAnnotation != null) {
System.out.println(" Method Name : " + method.getName());
System.out.println(" Author : " + todoAnnotation.author());
System.out.println(" Priority : " + todoAnnotation.priority());
System.out.println(" Status : " + todoAnnotation.status());
}
java注解是怎么实现的
注解的使用一般是与java的反射一起使用,下面是一个例子
注解相当于一种标记,在程序中加了注解就等于为程序打上了某种标记,没加,则等于没有某种标记,以后,javac编译器,开发工具和其他程序可以用反射来了解你的类及各种元素上有无何种标记,看你有什么标记,就去干相应的事。标记可以加在包,类,字段,方法,方法的参数以及局部变量上。
自定义注解及其应用
1)、定义一个最简单的注解
public @interface MyAnnotation {
//......
}
2)、把注解加在某个类上:
@MyAnnotation
public class AnnotationTest{
//......
}
以下为模拟案例
自定义注解@MyAnnotation
1 package com.ljq.test;
2
3 import java.lang.annotation.ElementType;
4 import java.lang.annotation.Retention;
5 import java.lang.annotation.RetentionPolicy;
6 import java.lang.annotation.Target;
7
8 /**
9 * 定义一个注解
10 *
11 *
12 * @author jiqinlin
13 *
14 */
15 //Java中提供了四种元注解,专门负责注解其他的注解,分别如下
16
17 //@Retention元注解,表示需要在什么级别保存该注释信息(生命周期)。可选的RetentionPoicy参数包括:
18 //RetentionPolicy.SOURCE: 停留在java源文件,编译器被丢掉
19 //RetentionPolicy.CLASS:停留在class文件中,但会被VM丢弃(默认)
20 //RetentionPolicy.RUNTIME:内存中的字节码,VM将在运行时也保留注解,因此可以通过反射机制读取注解的信息
21
22 //@Target元注解,默认值为任何元素,表示该注解用于什么地方。可用的ElementType参数包括
23 //ElementType.CONSTRUCTOR: 构造器声明
24 //ElementType.FIELD: 成员变量、对象、属性(包括enum实例)
25 //ElementType.LOCAL_VARIABLE: 局部变量声明
26 //ElementType.METHOD: 方法声明
27 //ElementType.PACKAGE: 包声明
28 //ElementType.PARAMETER: 参数声明
29 //ElementType.TYPE: 类、接口(包括注解类型)或enum声明
30
31 //@Documented将注解包含在JavaDoc中
32
33 //@Inheried允许子类继承父类中的注解
34
35
36 @Retention(RetentionPolicy.RUNTIME)
37 @Target({ElementType.METHOD, ElementType.TYPE})
38 public @interface MyAnnotation {
39 //为注解添加属性
40 String color();
41 String value() default "我是林计钦"; //为属性提供默认值
42 int[] array() default {1, 2, 3};
43 Gender gender() default Gender.MAN; //添加一个枚举
44 MetaAnnotation metaAnnotation() default @MetaAnnotation(birthday="我的出身日期为1988-2-18");
45 //添加枚举属性
46
47 }
注解测试类AnnotationTest
1 package com.ljq.test;
2
3 /**
4 * 注解测试类
5 *
6 *
7 * @author jiqinlin
8 *
9 */
10 //调用注解并赋值
11 @MyAnnotation(metaAnnotation=@MetaAnnotation(birthday = "我的出身日期为1988-2-18"),color="red", array={23, 26})
12 public class AnnotationTest {
13
14 public static void main(String[] args) {
15 //检查类AnnotationTest是否含有@MyAnnotation注解
16 if(AnnotationTest.class.isAnnotationPresent(MyAnnotation.class)){
17 //若存在就获取注解
18 MyAnnotation annotation=(MyAnnotation)AnnotationTest.class.getAnnotation(MyAnnotation.class);
19 System.out.println(annotation);
20 //获取注解属性
21 System.out.println(annotation.color());
22 System.out.println(annotation.value());
23 //数组
24 int[] arrs=annotation.array();
25 for(int arr:arrs){
26 System.out.println(arr);
27 }
28 //枚举
29 Gender gender=annotation.gender();
30 System.out.println("性别为:"+gender);
31 //获取注解属性
32 MetaAnnotation meta=annotation.metaAnnotation();
33 System.out.println(meta.birthday());
34 }
35 }
36 }
枚举类Gender,模拟注解中添加枚举属性
1 package com.ljq.test;
2 /**
3 * 枚举,模拟注解中添加枚举属性
4 *
5 * @author jiqinlin
6 *
7 */
8 public enum Gender {
9 MAN{
10 public String getName(){return "男";}
11 },
12 WOMEN{
13 public String getName(){return "女";}
14 }; //记得有“;”
15 public abstract String getName();
16 }
注解类MetaAnnotation,模拟注解中添加注解属性
1 package com.ljq.test;
2
3 /**
4 * 定义一个注解,模拟注解中添加注解属性
5 *
6 * @author jiqinlin
7 *
8 */
9 public @interface MetaAnnotation {
10 String birthday();
11 }
java元注解解析的介绍就聊到这里吧,感谢你花时间阅读本站内容,更多关于java常用元注解、java元注解解析的信息别忘了在本站进行查找喔。
发布于:2022-12-16,除非注明,否则均为
原创文章,转载请注明出处。