盘点Java框架常用的3大底层技术!
这段时间很久没有更新自己的博客了,对自己的不严谨和不自律批评下自己,以后更加严格要求自己。
这篇博客像往常一样主要复习底层知识点,本文所介绍的三个Java底层技术,有着逐渐递进的特点,Java注解中使用了JDK动态代理,而JDK动态代理中运用了Java反射。
同样的是通过架构师这个微信公众号来学习的。
Java注解
当我们阅读框架源码时,会看到其中包含着大量的注解,注解被广泛使用的原因在于,可以生成一些通用的“模板化”代码,来避免重复性的工作。使用注解的工作模式是,通过注解来描述我们的意图,然后用注解解析工具对注解进行解析。
实验:自定义注解
首先我们通过@interface
关键字定义一个注解@Tree
,定义注解时,需要定义两个内容:元注解,注解属性。
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Tree {
String name() default "tree";
}
元注解:
可以看到我在上面还添加了@Target
和@Retention
,这个是元注解,也就是添加到注解之上的注解,元注解有5种:
@Retention:声明注解的的存活时间
RetentionPolicy.SOURCE 注解只在源码阶段保留,在编译器进行编译时它将被丢弃忽视。
RetentionPolicy.CLASS 注解只被保留到编译进行的时候,它并不会被加载到 JVM 中。
RetentionPolicy.RUNTIME 注解可以保留到程序运行的时候,它会被加载进入到 JVM 中,所以在程序运行时可以获取到它们。
@Target:声明注解运用的地方
ElementType.ANNOTATION_TYPE 应用到注解
ElementType.CONSTRUCTOR 应用到构造方法
ElementType.FIELD 应用到属性
ElementType.LOCAL_VARIABLE 应用到局部变量
ElementType.METHOD 应用到方法
ElementType.PACKAGE 应用到包
ElementType.PARAMETER 应用到方法内的参数
ElementType.TYPE 应用到类型(类、接口、枚举)
@Documented:将注解中的元素包含到 Javadoc 中
@Inherited:使用了这个注解的子类,就继承了该注解
@Repeatable:Java1.8新增特性,应用于注解的值可以取多个的场景
注解属性:
可以类比为普通类中的成员变量,注解中只有成员变量没有方法,在使用该注解时为该属性赋值,也可以在定义时赋默认值。
【注解处理器】
在注解处理器中,我们可以为注解定义逻辑,例如在下面的例子中,就是调用AnnotationClient类中所有方法,把@Tree中name
属性值注入到方法中。
public class TreeProcessor {
public void parseMethod(final Class<?> clazz) throws Exception {
final Object obj = clazz.getConstructor(new Class[] {}).newInstance(new Object[] {});
final Method[] methods = clazz.getDeclaredMethods();
for (final Method method : methods) {
final Tree my = method.getAnnotation(Tree.class);
if (null != my) {
method.invoke(obj, my.name());
}
}
}
}
接下来做一下测试:
public class AnnotationClient {
@Tree
public static void sayHello(final String name) {
System.out.println("==>> Hi, " + name + " [sayHello]");
}
@Tree(name = "Someone")
public static void sayHelloToSomeone(final String name) {
System.out.println("==>> Hi, " + name + " [sayHelloToSomeone]");
}
public static void main(final String[] args) throws Exception {
final TreeProcessor treeProcessor = new TreeProcessor();
treeProcessor.parseMethod(AnnotationClient.class);
}
}
实现:
==>> Hi, Someone [syaHelloToSomeone]
==>> Hi, tree [sayHello]
深入理解注解
如果换一个角度理解注解:它的本质是一个继承了Annotation接口的接口
当我们通过getAnnotation()
方法获取一个注解的时候,JDK会通过动态代理生成注解的代理类$Proxy1,这个代理类代理了Tree中的所有方法,其实本质上还是通过反射来实现的,但是我们逐步递进的分析,先研究注解,下一步研究JDK动态代理,最后才能到达反射。
JAVA 中专门用于处理注解的 Handler:
sun.reflect.annotation.AnnotationInvocationHandler
AnnotationInvocationHandler
有如下几个属性:
private final Class<? extends Annotation> type;
private final Map<String, Object> memberValues;
private transient volatile Method[] memberMethods = null;
其中memberValues在初始化后,key是注解的属性值,value是我们为属性的赋值,可能你已经忘了我们在程序中是怎么做的了 : @Tree(name = "Someone")
所有动态代理类生成的方法,都会走这个invoke()
方法。
而这个invoke方法做的事情,概括起来就是:通过方法名获取属性值。
public Object invoke(Object var1, Method var2, Object[] var3) {
String var4 = var2.getName();