注解: 提供一种为程序设置元数据的方法
元数据:
- 是添加到程序元素如:字段,方法,类,包上的额外信息。
- 注解是一种分散式的元数据设置方式,xml是一种集中式的元数据设置方式。
- 注解的基本原则:注解不能直接干扰到程序代码运行
注解的分类: - 标准注解: 如 Override @Deprecated(功能就是不推荐使用或者已经不在维护或者使用有风险的) @SuppressWarnings
(忽列某一个warning)
元注解(用于修饰注解的注解,通常定义在注解上) Retention Target() @Inherited @Documented)
自定义注解 :根据实际需要定义一些特定的注解。
注解的作用: - 作为特定的标记,用于告知编译器一些信息。
- 编译时动态处理,如生成代码,如lombok的@Data.
- 运行时动态处理,作为额外信息载体,如获取注解信息
自定义注解继承java.lang.annotation.Annotation
使用
javac 加要编译的java文件
在使用
javap -verbose 加上之前编译好的class文件
查看反编译的结果
public interface com.example.annotations.UserAnnotation extends java.lang.annotation.Annotation
Target分析:
Target注解的作用目标。有以下:
public enum ElementType {
/**类声明 */
TYPE,
/** 成员变量声明 */
FIELD,
/** 方法声明 */
METHOD,
/**参数声明 */
PARAMETER,
/**构造器 */
CONSTRUCTOR,
/**局部变量声明 */
LOCAL_VARIABLE,
/** 注解声明(应用于另一个注解上) */
ANNOTATION_TYPE,
/** 包声明 */
PACKAGE,
/**
* 类型参数声明
*/
TYPE_PARAMETER,
/**
* 类型使用声明
*/
TYPE_USE
}
Retention(保留):
Retention需要传入RetentionPolicy(保留政策)的枚举类(默认是class):
public enum RetentionPolicy {
/**
*注解将被编译器丢弃(该类型的注解信息只会保留在源码里,源码经过编译 后,注解信息会被丢弃,不会保留在编译好的class文件里)
*/
SOURCE,
/**
注解在class文件中可用,但会被VM丢弃(该类型的注解信息会保留在源码里和class文件里,在执行的时候,不会加载到虚拟机(JVM)中)
*/
CLASS,
/**
VM将在运行期也保留注解信息,因此可以通过反射机制读取注解的信息(源码、class文件和执行的时候都有注解的信息)
*/
RUNTIME
}
@Documented 表示注解会被包含在javaapi文档中
@Inherited 允许子类继承父类的注解
自定义注解的实例:
自定义一个UserAnnotation实例作用在类上和方法及成员变量,注意自定义注解不能继承也不能实现,否则编译不过。
@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface UserAnnotation {
public String name() default "张三";
public int age() default 90;
public String gender();
public String addr();
public String desc() default "简单的描述";
}
注解的使用:
package com.example.annotations;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
@UserAnnotation(name = "作用在类上的UserAnnotation的名字", age = 100, gender = "作用在类上的", addr = "作用在类上的哦",
desc = "作用在类上的")
public class UserInfo {
private String name;
private int age;
@UserAnnotation(gender = "作用在成员变上的UserAnnotation",addr = "在成员变量上的注解哦")
private String gender;
private String desc;
private String addr;
@UserAnnotation(gender = "作用在方法上的UserAnnotation",addr = "在方法上的注解哦")
public void analysis() {
System.out.println("测试java注解");
}
public static void main(String[] args) throws ClassNotFoundException {
AnnotationUtils.annotationByClass();
AnnotationUtils.annotationByMethod();
AnnotationUtils.annotationByFiled();
}
}
如果不同过反射来对这些注解进行操作,实际上注解是对程序没有多大的作用,需要通过方式来对注解的信息进行操作,如通过自定义三个方法(通过反射)解析注解在类上和成员变量及方法上的三个方法:
package com.example.annotations;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
/**
* 解析作用在类上的注解
*/
public class AnnotationUtils {
public static void annotationByClass() throws ClassNotFoundException {
// 通过反射的方式获取这个UserInfo的信息
Class<?> aClass = Class.forName("com.example.annotations.UserInfo");
// 获取作用在UserInfo类上的注解Annotation
Annotation[] annotations = aClass.getAnnotations();
for (Annotation annotation : annotations) {
UserAnnotation userAnnotation = (UserAnnotation) annotation;
System.out.println("name:" + userAnnotation.name() + "\n" + "age:" + userAnnotation.age() + "\n"
+ "addr:" + userAnnotation.addr() + "\n" + "gender:" + userAnnotation.gender() + "\n" +
"desc:" + userAnnotation.desc());
}
}
public static void annotationByMethod() throws ClassNotFoundException {
// 通过反射的方式获取这个UserInfo的信息
Class<?> aClass = Class.forName("com.example.annotations.UserInfo");
Method[] declaredMethods = aClass.getDeclaredMethods();
for (Method method : declaredMethods) {
boolean hashAnnotation = method.isAnnotationPresent(UserAnnotation.class);
if (hashAnnotation) {
UserAnnotation userAnnotation = method.getAnnotation(UserAnnotation.class);
System.out.println("name:" + userAnnotation.name() + "\n" + "age:" + userAnnotation.age() + "\n"
+ "addr:" + userAnnotation.addr() + "\n" + "gender:" + userAnnotation.gender() + "\n" +
"desc:" + userAnnotation.desc());
}
}
}
public static void annotationByFiled() throws ClassNotFoundException {
// 通过反射的方式获取这个UserInfo的信息
Class<?> aClass = Class.forName("com.example.annotations.UserInfo");
Field[] fields = aClass.getDeclaredFields();
for (Field field : fields) {
boolean hashPresent = field.isAnnotationPresent(UserAnnotation.class);
if (hashPresent) {
UserAnnotation userAnnotation = field.getAnnotation(UserAnnotation.class);
System.out.println("name:" + userAnnotation.name() + "\n" + "age:" + userAnnotation.age() + "\n"
+ "addr:" + userAnnotation.addr() + "\n" + "gender:" + userAnnotation.gender() + "\n" +
"desc:" + userAnnotation.desc());
}
}
}
}
注解原理
通过设置jvm参数为-Dsun.misc.ProxyGenerator.saveGeneratedFiles=true -XX:+TraceClassLoading
通过在idea里找到这个测试类的Edit Configurations 点击进去然后设置,然后在VM options 设置参数-Dsun.misc.ProxyGenerator.saveGeneratedFiles=true
表示通过jdk动态代理生成器。运行该配置的main方法,则jdk动态代理生成器会在根目录生成一个com.sun.proxy下会有
P
r
o
x
y
0.
c
l
a
s
s
和
Proxy0.class和
Proxy0.class和Proxy1.class的代理对象。
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package com.sun.proxy;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
public final class $Proxy0 extends Proxy implements Retention {
private static Method m1;
private static Method m2;
private static Method m4;
private static Method m0;
private static Method m3;
public $Proxy0(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 RetentionPolicy value() throws {
try {
return (RetentionPolicy)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("java.lang.annotation.Retention").getMethod("annotationType");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
m3 = Class.forName("java.lang.annotation.Retention").getMethod("value");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
从上面$Proxy0 可知,public final class KaTeX parse error: Expected '}', got 'EOF' at end of input: …究java注解的原理,直接查看Proxy1。从$Proxy1中public final class $Proxy1 extends Proxy implements UserAnnotation
实现了我们上文定义的UserAnnotation的注解。查看源码$Proxy1定义了从m0到m8九个静态成员变量,代码如下:
private static Method m1;
private static Method m7;
private static Method m5;
private static Method m2;
private static Method m6;
private static Method m4;
private static Method m8;
private static Method m3;
private static Method m0;
这九个静态成员变量一一对应着:
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m7 = Class.forName("com.example.annotations.UserAnnotation").getMethod("name");
m5 = Class.forName("com.example.annotations.UserAnnotation").getMethod("addr");
m2 = Class.forName("java.lang.Object").getMethod("toString");
m6 = Class.forName("com.example.annotations.UserAnnotation").getMethod("gender");
m4 = Class.forName("com.example.annotations.UserAnnotation").getMethod("age");
m8 = Class.forName("com.example.annotations.UserAnnotation").getMethod("annotationType");
m3 = Class.forName("com.example.annotations.UserAnnotation").getMethod("desc");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
除了m0的hashCode和m1的Object及m2的toString的成员变量以外其他六个都是UserAnnotation的成员变量.通过查看$Proxy1的构造函数:
public $Proxy1(InvocationHandler var1) throws {
super(var1);
}
构造函数传入InvocationHandler对象并传给super(var1),通过查看super()的源码:
protected Proxy(InvocationHandler h) {
Objects.requireNonNull(h);
this.h = h;
}
super()实际就是InvocationHandler本身.通过阅读代码发现m0到m8都会对应一一对应着一个方法,并且方法的返回值就是成员变量的类型。以m3的desc为例:
m3 = Class.forName("com.example.annotations.UserAnnotation").getMethod("desc");
public final String desc() throws {
try {
return (String)super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
点击desc()方法里的invoke(),进去了以后发现
public interface InvocationHandler
只是一个接口invoke没有具体的实现,和上文中的一样在VM options后面打一个空格然后接上
-XX:+TraceClassLoading
查看那个这个main用多少类,以达到追踪到那个实现类实现了InvocationHandler接口,控制台的打印结果中发现
[Loaded sun.reflect.annotation.AnnotationInvocationHandler from /app/jdk1.8/jre/lib/rt.jar]
是AnnotationInvocationHandler继承了InvocationHandler并实现了其中的invoke方法.其中invke的源码如下:
public Object invoke(Object var1, Method var2, Object[] var3) {
String var4 = var2.getName();
Class[] var5 = var2.getParameterTypes();
if (var4.equals("equals") && var5.length == 1 && var5[0] == Object.class) {
return this.equalsImpl(var3[0]);
} else if (var5.length != 0) {
throw new AssertionError("Too many parameters for an annotation method");
} else {
byte var7 = -1;
switch(var4.hashCode()) {
case -1776922004:
if (var4.equals("toString")) {
var7 = 0;
}
break;
case 147696667:
if (var4.equals("hashCode")) {
var7 = 1;
}
break;
case 1444986633:
if (var4.equals("annotationType")) {
var7 = 2;
}
}
switch(var7) {
case 0:
return this.toStringImpl();
case 1:
return this.hashCodeImpl();
case 2:
return this.type;
default:
Object var6 = this.memberValues.get(var4);
if (var6 == null) {
throw new IncompleteAnnotationException(this.type, var4);
} else if (var6 instanceof ExceptionProxy) {
throw ((ExceptionProxy)var6).generateException();
} else {
if (var6.getClass().isArray() && Array.getLength(var6) != 0) {
var6 = this.cloneArray(var6);
}
return var6;
}
}
}
}
其中除了toString,还有hashCode,type外,var6需要从memberValues中取值,而memberValues是一个map的键值对:
private final Map<String, Object> memberValues;
memberValues存储了UserAnnotation的属性值,key注解的成员变量的名称,vaule则为注解的值.拿到map的值后会判断如果是Array数组则返回数组的副本,
注解原理:若为单个则返回单个元素.
从上面的代跟踪可以得出以下注解的实际运行原理如下:
1 通过键值对的形式为注解属性赋值
2 在编译器检查注解的使用范围,将注解信息写入元素属性表中
3 运行时jvm将runtime所有注解属性取出来并最终存在map中
4 然后创建AnnotationInvocationHandler实例并传入前面的map